Question

J'essaie d'analyser la date retournée en valeur du HTML5 datetime champ de saisie. Essayez-le dans Opera pour voir un exemple. La date retournée ressemble à ceci: 2011-05-03T11:58:01Z.

Je voudrais analyser cela dans une date Java ou un objet calendrier.

Idéalement, une solution devrait avoir les choses suivantes:

  • Pas de bibliothèques externes (pots)
  • Gère tous les formats RFC 3339 acceptables
  • Une chaîne doit pouvoir être facilement validée pour voir s'il s'agit d'une date RFC 3339 valide
Était-ce utile?

La solution

Je viens de trouver que Google a implémenté l'analyseur RFC3339 dans la bibliothèque client Google HTTP

https://github.com/google/google-http-java-lient/blob/dev/google-http-client/src/main/java/com/google/api/client/util/datetime.java

Testé. Il fonctionne bien pour analyser les fragments de temps inférieurs aux secondes.

import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;

import com.google.api.client.util.DateTime;

DateTimeFormatter formatter = DateTimeFormatter
            .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
            .withZone(ZoneId.of("UTC"));

@Test
public void test1e9Parse() {
    String timeStr = "2018-04-03T11:32:26.553955473Z";

    DateTime dateTime = DateTime.parseRfc3339(timeStr);
    long millis = dateTime.getValue();

    String result = formatter.format(new Date(millis).toInstant());

    assert result.equals("2018-04-03T11:32:26.553Z");
}

@Test
public void test1e3Parse() {
    String timeStr = "2018-04-03T11:32:26.553Z";

    DateTime dateTime = DateTime.parseRfc3339(timeStr);
    long millis = dateTime.getValue();

    String result = formatter.format(new Date(millis).toInstant());

    assert result.equals("2018-04-03T11:32:26.553Z");
}

@Test
public void testEpochSecondsParse() {

    String timeStr = "2018-04-03T11:32:26Z";

    DateTime dateTime = DateTime.parseRfc3339(timeStr);
    long millis = dateTime.getValue();

    String result = formatter.format(new Date(millis).toInstant());

    assert result.equals("2018-04-03T11:32:26.000Z");
}

Autres conseils

tl; dr

Instant.parse( "2011-05-03T11:58:01Z" )

ISO 8601

Réellement, RFC 3339 n'est qu'un simple «profil» autoproclamé de la norme réelle, ISO 8601.

La RFC est différente en ce qu'elle viole délibérément ISO 8601 pour permettre un décalage négatif de zéro heures (-00:00) et donne une signification sémantique de «décalage inconnu». Ce sémantique me semble être une très mauvaise idée. Je conseille de rester avec les règles ISO 8601 les plus sensibles. Dans ISO 8601, n'ayant pas de décalage du tout signifie que le décalage est inconnu - une signification évidente, tandis que la règle RFC est abstruse.

Le moderne Java.Time Les classes utilisent les formats ISO 8601 par défaut lors des chaînes d'analyse / génération.

Votre chaîne d'entrée représente un moment dans UTC. La Z À la fin, c'est court pour Zulu et signifie UTC.

Instant (ne pas Date)

La classe moderne Instant représente un moment dans l'UTC. Cette classe remplace java.util.Date, et utilise une résolution plus fine des nanosecondes plutôt que des millisecondes.

Instant instant = Instant.parse( "2011-05-03T11:58:01Z" ) ;

ZonedDateTime (ne pas Calendar)

Pour voir le même moment à travers le temps mural utilisé par les habitants d'une certaine région (un fuseau horaire), appliquez un ZoneId Pour obtenir un ZonedDateTime. Cette classe ZonedDateTime remplace le java.util.Calendar classer.

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment, same point on the timeline, different wall-clock time.

Conversion

Je recommande fortement d'éviter les cours de date d'heure hérités lorsque cela est possible. Mais si vous devez interopérer avec un ancien code non encore mis à jour Java.Time, vous pouvez convertir des allers-retours. Appelez de nouvelles méthodes ajoutées au Agé de Des classes.

Instant remplace java.util.Date.

java.util.Date myJUDate = java.util.Date.from( instant ) ;  // From modern to legacy.
Instant instant = myJUDate.toInstant() ;                    // From legacy to modern.

ZonedDateTime remplace GregorianCalendar.

java.util.GregorianCalendar myGregCal = java.util.GregorianCalendar.from( zdt ) ;  // From modern to legacy.
ZonedDateTime zdt = myGregCal.toZonedDateTime() ;           // From legacy to modern.

Si tu as un java.util.Calendar c'est en fait un GregorianCalendar, moulage.

java.util.GregorianCalendar myGregCal = ( java.util.GregorianCalendar ) myCal ;  // Cast to the concrete class.
ZonedDateTime zdt = myGregCal.toZonedDateTime() ;           // From legacy to modern.

Préoccupations à puces

Concernant les problèmes spécifiques de votre question…

  • Pas de bibliothèques externes (pots)

La Java.Time Les cours sont intégrés à Java 8, 9, 10 et plus tard. Une implémentation est également incluse dans Android ultérieure. Pour Java antérieure et Android plus tôt, consultez la section suivante de cette réponse.

  • Gère tous les formats RFC 3339 acceptables

Les différents Java.Time Les cours gèrent chaque format ISO 8601 que je connais. Ils gèrent même des formats qui ont mystérieusement disparu des éditions ultérieures de la norme.

Pour d'autres formats, voir le parse et toString Méthodes des différentes classes telles que LocalDate, OffsetDateTime, etc. Aussi, recherchez la pile déborde comme il y a de nombreux Exemples et discussions sur ce sujet.

  • Une chaîne doit pouvoir être facilement validée pour voir s'il s'agit d'une date RFC 3339 valide

Pour valider les chaînes d'entrée, piéger pour DateTimeParseException.

try {
    Instant instant = Instant.parse( "2011-05-03T11:58:01Z" ) ;
} catch ( DateTimeParseException e ) {
    … handle invalid input
}

À propos de Java.Time

La Java.Time Le cadre est intégré à Java 8 et plus tard. Ces classes supplantent l'ancien gênant héritage des cours de date de date tels que java.util.Date, Calendar, & SimpleDateFormat.

La Joda-temps Project, maintenant dans Mode de Maintenance, conseille la migration vers le Java.Time Des classes.

Pour en savoir plus, voir le Tutoriel Oracle. Et la recherche de pile déborde pour de nombreux exemples et explications. La spécification est JSR 310.

Vous pouvez échanger Java.Time objets directement avec votre base de données. Utiliser un Pilote JDBC conforme à JDBC 4.2 ou plus tard. Pas besoin de chaînes, pas besoin de java.sql.* Des classes.

Où obtenir les classes java.time?

La Threeten-Extra Le projet étend Java.time avec des classes supplémentaires. Ce projet est un terrain d'essai pour d'éventuels ajouts futurs à Java.Time. Vous pouvez trouver des cours utiles ici comme Interval, YearWeek, YearQuarter, et Suite.

Donc, en principe, cela serait fait en utilisant différents Simpledateformat motifs.

Ici une liste de modèles pour le Déclarations individuelles dans RFC 3339:

  • Date-Filhyear: yyyy
  • Date-mois: MM
  • DATE-MDAY: dd
  • Heure d'heure: HH
  • Temps -minue: mm
  • temps-temps: ss
  • Time-SecFrac: .SSS (S signifie cependant la milliseconde - il n'est pas clair ce qui se passerait s'il y en a plus ou moins de 3 chiffres.)
  • Time-Numoffset: (comme +02:00 semble ne pas être pris en charge - mais il prend en charge les formats +0200, GMT+02:00 et quelques fuseaux horaires nommés en utilisant z et Z.)
  • décalage du temps: 'Z' (ne pas prendre en charge d'autres fuseaux horaires) - vous devez utiliser format.setTimezone(TimeZone.getTimeZone("UTC")) avant de l'utiliser.)
  • temps partiel: HH:mm:ss ou HH:mm:ss.SSS.
  • à plein temps: HH:mm:ss'Z' ou HH:mm:ss.SSS'Z'.
  • date complète: yyyy-MM-dd
  • Date d'heure: yyyy-MM-dd'T'HH:mm:ss'Z' ou yyyy-MM-dd'T'HH:mm:ss.SSS'Z'

Comme nous pouvons le voir, cela ne semble pas être en mesure de tout analyser. Ce serait peut-être une meilleure idée de mettre en œuvre un RFC3339DateFormat à partir de zéro (en utilisant des expressions régulières, pour la simplicité ou l'analyse à la main, pour l'efficacité).

Ici est une méthode simple pour le faire. Cela peut répondre à vos besoins.

Peut-être pas la manière la plus élégante, mais en travaillant certainement une que j'ai récemment faite:

Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");
cal.setTime(sdf.parse(dateInString.replace("Z", "").replace("T", "-")));

Avec le format, vous avez, par exemple, 2011-05-03T11: 58: 01Z, le code ci-dessous fera l'affaire. Cependant, j'ai récemment essayé HTML5 Datetime dans Chrome et Opera, cela me donne 2011-05-03T11: 58Z -> Je n'ai pas la partie SS qui ne peut pas être gérée par le code ci-dessous.

new Timestamp(javax.xml.datatype.DatatypeFactory.newInstance().newXMLGregorianCalendar(date).toGregorianCalendar().getTimeInMillis());
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").parse(datetimeInFRC3339format)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top