web-dev-qa-db-fra.com

Analyse Android String to Date - Caractère de modèle inconnu 'X'

J'ai Service extraire la chaîne de date sur le Web et je veux ensuite l'assimiler à l'objet Date. Mais d’une manière ou d’une autre, l’application se bloque . C’est ma chaîne que je suis en train d’analyser: 2015-02-05T05:20:02+00:00

onStartCommand ()

String datetime = "2015-02-05T05:20:02+00:00";
Date new_date = stringToDate(datetime);

stringToDate ()

private Date stringToDate(String s){
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
    try{
        return df.parse(s);
    }catch(ParseException e){
        e.printStackTrace();
    }
    return null;
}

LogCat:

02-06 20:37:02.008: E/AndroidRuntime(28565): FATAL EXCEPTION: main
02-06 20:37:02.008: E/AndroidRuntime(28565): Process: com.dotmav.runescapenotifier, PID: 28565
02-06 20:37:02.008: E/AndroidRuntime(28565): Java.lang.RuntimeException: Unable to start service com.dotmav.runescapenotifier.GEService@384655b5 with Intent { cmp=com.dotmav.runescapenotifier/.GEService }: Java.lang.IllegalArgumentException: Unknown pattern character 'X'
02-06 20:37:02.008: E/AndroidRuntime(28565):    at Android.app.ActivityThread.handleServiceArgs(ActivityThread.Java:2881)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at Android.app.ActivityThread.access$2100(ActivityThread.Java:144)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at Android.app.ActivityThread$H.handleMessage(ActivityThread.Java:1376)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at Android.os.Handler.dispatchMessage(Handler.Java:102)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at Android.os.Looper.loop(Looper.Java:135)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at Android.app.ActivityThread.main(ActivityThread.Java:5221)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at Java.lang.reflect.Method.invoke(Native Method)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at Java.lang.reflect.Method.invoke(Method.Java:372)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:899)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:694)
02-06 20:37:02.008: E/AndroidRuntime(28565): Caused by: Java.lang.IllegalArgumentException: Unknown pattern character 'X'
02-06 20:37:02.008: E/AndroidRuntime(28565):    at Java.text.SimpleDateFormat.validatePatternCharacter(SimpleDateFormat.Java:314)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at Java.text.SimpleDateFormat.validatePattern(SimpleDateFormat.Java:303)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at Java.text.SimpleDateFormat.<init>(SimpleDateFormat.Java:356)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at Java.text.SimpleDateFormat.<init>(SimpleDateFormat.Java:249)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at com.dotmav.runescapenotifier.GEService.stringToDate(GEService.Java:68)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at com.dotmav.runescapenotifier.GEService.onStartCommand(GEService.Java:44)
02-06 20:37:02.008: E/AndroidRuntime(28565):    at Android.app.ActivityThread.handleServiceArgs(ActivityThread.Java:2864)
02-06 20:37:02.008: E/AndroidRuntime(28565):    ... 9 more

EDIT: onDestroy () définit l'alarme pour la mise à jour périodique ...

AlarmManager alarm = (AlarmManager)getSystemService(ALARM_SERVICE);
alarm.set(
    AlarmManager.RTC_WAKEUP,
    System.currentTimeMillis() + (1000 * 60),
    PendingIntent.getService(this, 0, new Intent(this, GEService.class), 0)
);
36
Matjaž Mav

Supprimer "XXX" de 

DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");

et tout fonctionnerait bien.

Parcourez la liste des symboles pouvant être utilisés dans un constructeur SimpleDateFormat. Ceci est la documentation

Vous recherchez probablement "yyyy-MM-dd'T'HH:mm:ss.SSSZ"

Changez votre code en 

DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); 

ou 

DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); // if timezone is required
24
Rohit5k2

La version Android de SimpleDateFormat ne prend pas en charge le modèle X, donc XXX ne fonctionnera pas. Vous pouvez plutôt utiliser ZZZZZ qui fait la même chose et affiche le fuseau horaire au format +02:00 (ou -02:00 en fonction du fuseau horaire local).

41
flawyte

Vous utilisez le mauvais formateur de date. 

Utilisez ceci à la place: DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");

Je pense que, contrairement à Java 7, Android utilise Z (comme dans Java 6) pour les fuseaux horaires et non X. Utilisez donc this pour vos formats de date.

11
Thanos

Puisque Z et XXX sont différents, j'ai implémenté la solution de contournement suivante:

// This is a workaround from Z to XXX timezone format
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ") {

    @Override
    public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) {
        StringBuffer rfcFormat = super.format(date, toAppendTo, pos);
        return rfcFormat.insert(rfcFormat.length() - 2, ":");
    }

    @Override
    public Date parse(String text, ParsePosition pos) {
        if (text.length() > 3) {
            text = text.substring(0, text.length() - 3) + text.substring(text.length() - 2);
        }
        return super.parse(text, pos);
    }
}
7
Alexander K

Personne n'a mentionné que cette erreur se produisait sur des périphériques pre-nougat. J'ai donc pensé à partager ma réponse, ce qui est peut-être utile pour ceux qui ont atteint ce fil. 

Cette réponse indique à juste titre que "X" n’est pris en charge que pour les périphériques Nougat +. Je vois encore que documentation suggère d'utiliser "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" sans savoir pourquoi ils ne rendent pas ce point explicite. 

Pour moi, yyyy-MM-dd'T'HH:mm:ssXXX fonctionnait bien jusqu'à ce que j'essaie de le tester sur un périphérique 6.0 et que celui-ci se bloque, ce qui m'a amené à effectuer des recherches sur ce sujet. Le remplacer par yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ a résolu le problème et fonctionne sur tous les périphériques 5.0+. 

6
Wahib Ul Haq

Android SimpleDateFormat est différent du SDK Java 7 et ne prend pas en charge l'analyse par X de l'ISO 8601. Vous pouvez utiliser les styles Z ou ZZZZ pour formater et définir le fuseau horaire à UTC par programme. Voici une classe util:

public class DateUtil {

    public static final String iso8601DatePattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ";
    public static final DateFormat iso8601DateFormat = new SimpleDateFormat(iso8601DatePattern);
    public static final TimeZone utcTimeZone = TimeZone.getTimeZone("UTC");

    static {
        iso8601DateFormat.setTimeZone(utcTimeZone);
    }

    public static String formatAsIso8601(Date date) {

        return iso8601DateFormat.format(date);
    }
}
6
Narek

L'erreur dit que simpleDateFormat ne reconnaît pas le caractère X. Si vous recherchez des millisecondes, il est représenté avec le caractère S.

DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
2
Adam W

tl; dr

long millisecondsSinceEpoch = OffsetDateTime.parse( "2015-02-05T05:20:02+00:00" ).plusHour( 1 ).toInstant().toEpochMilli()  // Warning: Possible loss of data in truncating nanoseconds to milliseconds. But not in this particular case.

Détails

Les autres réponses sont correctes mais maintenant obsolètes. Les anciennes classes date-heure sont maintenant héritées. Utilisez plutôt les classes Java.time.

ISO 8601

La chaîne d'entrée est au format standard ISO 8601. Analyser directement, pas besoin de définir un modèle de formatage car les classes Java.time utilisent les formats ISO 8601 par défaut.

OffsetDateTime

L'entrée inclut un décalage de UTC avec le +00:00 afin que nous puissions analyser comme un objet OffsetDateTime .

String input = "2015-02-05T05:20:02+00:00" ;
OffsetDateTime odt = OffsetDateTime.parse( input );

Math

Si vous souhaitez ajouter une heure ou une minute plus tard pour définir une alarme, appelez les méthodes plus.

OffsetDateTime minuteLater = odt.plusMinutes( 1 );
OffsetDateTime hourLater = odt.plusHours( 1 );

Pour obtenir un nombre de millisecondes, passez par la classe Instant. La classe Instant représente un moment sur la ligne temporelle dans UTC avec une résolution de nanosecondes . Demander des millisecondes signifie perte de données possible lorsque les neuf chiffres d'une fraction décimale sont tronqués aux trois chiffres de la fraction décimale.

long millisecondsSinceEpoch = odt.toInstant().toEpochMilli();  // Warning: Possible loss of data in truncating nanoseconds to milliseconds.

À propos de Java.time

Le cadre Java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciennes classes de date-heure, telles que Java.util.Date, .Calendar et Java.text.SimpleDateFormat

Le projet Joda-Time , désormais en mode maintenance , conseille la migration vers Java.time.

Pour en savoir plus, consultez le Tutoriel Oracle . Et recherchez Stack Overflow pour de nombreux exemples et explications.

Une grande partie de la fonctionnalité Java.time est rétroportée vers Java 6 et 7 dans ThreeTen-Backport et ensuite adaptée à Android dans ThreeTenABP .

Le projet ThreeTen-Extra étend Java.time avec des classes supplémentaires. Ce projet est un terrain d'essai pour d'éventuels ajouts à Java.time. 

1
Basil Bourque

Utilisez une SimpleDateFormat pour produire une sortie String correctement formatée:

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));

String formattedNow = simpleDateFormat.format(new Date(System.currentTimeMillis()));

Sortie: 2018-02-27T07:36:47.686Z

1
suhasini

Solution simple: 

Utilisez this yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ 

Au lieu de cela de yyyy-MM-dd'T'HH:mm:ss.SSSXXX

Terminé.

1
Hiren Patel

La classe suivante peut être utilisée pour convertir la chaîne en date lorsque le modèle n'en est pas conscient.

    import Java.text.SimpleDateFormat;
    import Java.util.Arrays;
    import Java.util.Date;
    import Java.util.List;

/**
 * StringToDateFormater is a concrete class for formatting and parsing dates in a locale-sensitive manner. It allows for
 * formatting (date → text), parsing (text → date), and normalization.
 * 
 * This class is mainly used for convert the date from string. It should be used only when date pattern doesn't aware of
 * it. 
 *
 */
public class StringToDateFormater extends SimpleDateFormat {

    private static final List<String> DATE_SUPPORTED_FORMAT_LIST = Arrays.asList("yyyyMMddHHmmss", "yyyyMMddHHmm",
            "yyyyMMddHHmm", "yyyyMMddHH", "yyyyMMdd", "yyyyMMddHHmmssSS");

    /**
     * 
     */
    private static final long serialVersionUID = -1732857502488169225L;

    /**
     * @param pattern
     */
    public StringToDateFormater() {
    }

    @Override
    public Date parse(String source) {
        Date date = null;

        SimpleDateFormat dateFormat = null;
        for (String format : DATE_SUPPORTED_FORMAT_LIST) {
            dateFormat = new SimpleDateFormat(format);
            try {
                return dateFormat.parse(source);
            } catch (Exception exception) {

            }

        }

        return date;
    }
}
0
Sudhakar