web-dev-qa-db-fra.com

Comment analyser / formater les dates avec LocalDateTime? (Java 8)

Java 8 a ajouté une nouvelle API Java.time == == pour travailler avec les dates et les heures ( JSR 31 ).

J'ai la date et l'heure sous forme de chaîne (par exemple, "2014-04-08 12:30"). Comment puis-je obtenir une instance LocalDateTime à partir de la chaîne donnée?

Après avoir fini de travailler avec l'objet LocalDateTime: Comment puis-je reconvertir l'occurrence LocalDateTime en une chaîne de même format que celui présenté ci-dessus?

299
micha

Date et heure de l'analyse syntaxique

Pour créer un objet LocalDateTime à partir d'une chaîne, vous pouvez utiliser la méthode statique LocalDateTime.parse() . Il faut une chaîne et un DateTimeFormatter en paramètre. DateTimeFormatter est utilisé pour spécifier le modèle de date/heure.

String str = "1986-04-08 12:30";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.parse(str, formatter);

Date et heure du formatage

Pour créer une chaîne formatée à partir d'un objet LocalDateTime, vous pouvez utiliser la méthode format().

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.of(1986, Month.APRIL, 8, 12, 30);
String formattedDateTime = dateTime.format(formatter); // "1986-04-08 12:30"

Notez que certains formats de date/heure couramment utilisés sont prédéfinis en tant que constantes dans DateTimeFormatter. Par exemple, l'utilisation de DateTimeFormatter.ISO_DATE_TIME pour formater l'instance LocalDateTime à partir de la racine entraînerait la chaîne "1986-04-08T12:30:00".

Les méthodes parse() et format() sont disponibles pour tous les objets liés à la date/heure (par exemple, LocalDate ou ZonedDateTime).

482
micha

Vous pouvez également utiliser LocalDate.parse() ou LocalDateTime.parse() sur un String sans lui attribuer de motif, si String est au format format ISO-8601 .

par exemple,

String strDate = "2015-08-04";
LocalDate aLD = LocalDate.parse(strDate);
System.out.println("Date: " + aLD);

String strDatewithTime = "2015-08-04T10:11:30";
LocalDateTime aLDT = LocalDateTime.parse(strDatewithTime);
System.out.println("Date with Time: " + aLDT);

Sortie ,

Date: 2015-08-04
Date with Time: 2015-08-04T10:11:30

et utilisez DateTimeFormatter uniquement si vous devez traiter d’autres modèles de date. Par exemple, dd MMM uuuu représente le jour du mois (deux chiffres), trois lettres du nom du mois (janvier, février, mars, ...) et une année à quatre chiffres:

DateTimeFormatter dTF = DateTimeFormatter.ofPattern("dd MMM uuuu");
String anotherDate = "04 Aug 2015";
LocalDate lds = LocalDate.parse(anotherDate, dTF);
System.out.println(anotherDate + " parses to " + lds);

Sortie

04 Aug 2015 parses to 2015-08-04

rappelez-vous également que l'objet DateTimeFormatter est bidirectionnel; il peut à la fois analyser l’entrée et formater la sortie.

String strDate = "2015-08-04";
LocalDate aLD = LocalDate.parse(strDate);
DateTimeFormatter dTF = DateTimeFormatter.ofPattern("dd MMM uuuu");
System.out.println(aLD + " formats as " + dTF.format(aLD));

Sortie

2015-08-04 formats as 04 Aug 2015

(voir complète liste des modèles pour le formatage et l'analyse DateFormatter )

  Symbol  Meaning                     Presentation      Examples
  ------  -------                     ------------      -------
   G       era                         text              AD; Anno Domini; A
   u       year                        year              2004; 04
   y       year-of-era                 year              2004; 04
   D       day-of-year                 number            189
   M/L     month-of-year               number/text       7; 07; Jul; July; J
   d       day-of-month                number            10

   Q/q     quarter-of-year             number/text       3; 03; Q3; 3rd quarter
   Y       week-based-year             year              1996; 96
   w       week-of-week-based-year     number            27
   W       week-of-month               number            4
   E       day-of-week                 text              Tue; Tuesday; T
   e/c     localized day-of-week       number/text       2; 02; Tue; Tuesday; T
   F       week-of-month               number            3

   a       am-pm-of-day                text              PM
   h       clock-hour-of-am-pm (1-12)  number            12
   K       hour-of-am-pm (0-11)        number            0
   k       clock-hour-of-am-pm (1-24)  number            0

   H       hour-of-day (0-23)          number            0
   m       minute-of-hour              number            30
   s       second-of-minute            number            55
   S       fraction-of-second          fraction          978
   A       milli-of-day                number            1234
   n       nano-of-second              number            987654321
   N       nano-of-day                 number            1234000000

   V       time-zone ID                zone-id           America/Los_Angeles; Z; -08:30
   z       time-zone name              zone-name         Pacific Standard Time; PST
   O       localized zone-offset       offset-O          GMT+8; GMT+08:00; UTC-08:00;
   X       zone-offset 'Z' for zero    offset-X          Z; -08; -0830; -08:30; -083015; -08:30:15;
   x       zone-offset                 offset-x          +0000; -08; -0830; -08:30; -083015; -08:30:15;
   Z       zone-offset                 offset-Z          +0000; -0800; -08:00;

   p       pad next                    pad modifier      1

   '       escape for text             delimiter
   ''      single quote                literal           '
   [       optional section start
   ]       optional section end
   #       reserved for future use
   {       reserved for future use
   }       reserved for future use
134
Sufiyan Ghori

Les deux réponses ci-dessus expliquent très bien la question concernant les modèles de chaîne. Cependant, juste au cas où vous travailleriez avec ISO 8601 , il n’est pas nécessaire d’appliquer DateTimeFormatter puisque LocalDateTime est déjà préparé pour cela:

Convertissez LocalDateTime en fuseau horaire Chaîne ISO8601

LocalDateTime ldt = LocalDateTime.now(); 
ZonedDateTime zdt = ldt.atZone(ZoneOffset.UTC); //you might use a different zone
String iso8601 = zdt.toString();

Convertissez une chaîne ISO8601 en un LocalDateTime

String iso8601 = "2016-02-14T18:32:04.150Z";
ZonedDateTime zdt = ZonedDateTime.parse(iso8601);
LocalDateTime ldt = zdt.toLocalDateTime();
32
Marcio Jasinski

L'analyse d'une chaîne avec la date et l'heure à un moment donné (Java l'appelle un " Instant ") est assez compliquée. Java s’est attaqué à ce problème dans plusieurs itérations. Le dernier, _Java.time_ et _Java.time.chrono_, couvre presque tous les besoins (sauf Dilatation du temps :)).

Cependant, cette complexité crée beaucoup de confusion.

La clé pour comprendre l'analyse de la date est la suivante:

Pourquoi Java a-t-il tant de façons d'analyser une date?

  1. Il existe plusieurs systèmes pour mesurer un temps. Par exemple, les calendriers historiques japonais ont été dérivés des plages de temps du règne de l'empereur ou de la dynastie. Ensuite, il y a par exemple Horodatage UNIX. Heureusement, tout le monde (des affaires) a réussi à utiliser la même chose.
  2. Historiquement, les systèmes étaient basculés de/vers, pour raisons diverses . Par exemple. du calendrier julien au calendrier grégorien de 1582. Les dates "occidentales" antérieures à cette date doivent donc être traitées différemment.
  3. Et bien sûr, le changement ne s’est pas produit immédiatement. Parce que le calendrier venait des quartiers généraux de certaines religions et d’autres parties de l’Europe qui croyaient en d’autres croyances, l’Allemagne, par exemple, n’a pas basculé avant 1700.

... et pourquoi est-ce que LocalDateTime, ZonedDateTime et al. si compliqué

  1. Il y a fuseaux horaires . Un fuseau horaire est fondamentalement une "bande" * [1] de la surface de la Terre dont les autorités respectent les mêmes règles en matière de décalage horaire. Cela inclut les règles de l'heure d'été.
    Les fuseaux horaires changent avec le temps pour différentes régions, principalement en fonction de qui conquiert qui. Et les règles d'un fuseau horaire changement dans le temps également.

  2. Il y a des décalages horaires. Ce n'est pas la même chose que les fuseaux horaires, car un fuseau horaire peut être p. Ex. "Prague", mais avec décalage entre l'heure d'été et l'heure d'hiver.
    Si vous obtenez un horodatage avec un fuseau horaire, le décalage peut varier en fonction de la période de l’année. Au cours de l’heure bissextile, l’horodatage peut indiquer 2 heures différentes. ne peut pas être converti de manière fiable.
    Remarque: Par horodatage , j'entends "une chaîne contenant une date et/ou une heure, éventuellement avec un fuseau horaire et/ou un décalage horaire".

  3. Plusieurs fuseaux horaires peuvent partager le même décalage horaire pour certaines périodes. Par exemple, le fuseau horaire GMT/UTC est identique au fuseau horaire "Londres" lorsque le décalage de l'heure d'été n'est pas en vigueur.

Pour compliquer un peu les choses (mais ce n'est pas très important pour votre cas d'utilisation):

  1. Les scientifiques observent la dynamique de la Terre, qui évolue dans le temps. sur cette base, ils ajoutent des secondes à la fin des années individuelles. (Donc, _2040-12-31 24:00:00_ peut être une date-heure valide.) Cela nécessite des mises à jour régulières des métadonnées que les systèmes utilisent pour que les conversions de date soient correctes. Par exemple. sous Linux, vous obtenez des mises à jour régulières des packages Java incluant ces nouvelles données.
  2. Les mises à jour ne conservent pas toujours le comportement précédent pour les horodatages historiques et futurs. Il peut donc arriver que l'analyse des deux horodatages autour d'un changement de fuseau horaire les comparant peut donner des résultats différents lors de l'exécution sur des versions différentes du logiciel. Cela s'applique également à la comparaison entre le fuseau horaire affecté et un autre fuseau horaire.

    Si cela devait causer un bogue dans votre logiciel, envisagez d’utiliser un horodatage ne comportant pas de règles aussi compliquées, comme horodatage UNIX .

  3. En raison de 7, pour les dates futures, nous ne pouvons pas convertir les dates exactement avec certitude. Ainsi, par exemple, l'analyse actuelle de _8524-02-17 12:00:00_ peut être désactivée quelques secondes après l'analyse suivante.

Les API de JDK pour cela ont évolué avec les besoins contemporains

  • Les premières versions Java ne contenaient que _Java.util.Date_, avec une approche un peu naïve, en supposant qu'il ne reste que l'année, le mois, le jour et l'heure. Cela n'a pas suffi rapidement.
  • En outre, les besoins des bases de données étant différents, le système _Java.sql.Date_ a été introduit assez tôt, avec ses propres limites.
  • Étant donné que ni l'un ni l'autre ne couvraient différents calendriers et fuseaux horaires, l'API Calendar a été introduite.
  • Cela ne couvrait toujours pas la complexité des fuseaux horaires. Et pourtant, la combinaison des API ci-dessus était vraiment pénible. Ainsi, en tant que Java les développeurs ont commencé à travailler sur des applications Web globales, les bibliothèques qui ciblaient la plupart des cas d'utilisation, tels que JodaTime, sont rapidement devenues populaires. JodaTime était le standard de facto pendant environ une décennie.
  • Mais le JDK n’intégrant pas JodaTime, le travail était donc un peu fastidieux. Ainsi, après une très longue discussion sur la façon d'aborder la question, JSR-31 a été créé principalement basé sur JodaTime .

Comment gérer cela dans Java _Java.time_

Déterminer à quel type analyser un horodatage

Lorsque vous utilisez une chaîne d'horodatage, vous devez savoir quelles informations elle contient. C'est le point crucial. Si vous n'obtenez pas ce droit, vous vous retrouvez avec des exceptions cryptiques telles que "Impossible de créer Instant" ou " Décalage de zone manquant "ou" identificateur de zone inconnue ", etc.

Contient-il la date et l'heure?

  1. At-il un décalage horaire?
    Un décalage temporel est la partie _+hh:mm_. Parfois, _+00:00_ peut être remplacé par Z en tant que 'temps zoulou', UTC en temps universel coordonné ou GMT en temps de Greenwich. Ceux-ci définissent également le fuseau horaire.
    Pour ces horodatages, vous utilisez OffsetDateTime .

  2. At-il un fuseau horaire?
    Pour ces horodatages, vous utilisez ZonedDateTime .
    La zone est spécifiée soit par

    • nom ("Prague", "heure normale du Pacifique", "PST"), ou
    • "zone ID" ("America/Los_Angeles", "Europe/London"), représentée par Java.time.ZoneId .

    La liste des fuseaux horaires est compilée par un "base de données TZ" , soutenu par ICAAN.

    Selon le javadoc de ZoneId, les identifiants de zone peuvent également être spécifiés en tant que Z et décalés. Je ne sais pas comment cela correspond à de vraies zones. Si l'horodatage, qui n'a qu'une TZ, tombe dans une heure bissextile de changement de décalage horaire, alors il est ambigu et l'interprétation est sujette à ResolverStyle, voir ci-dessous.

  3. S'il n'a ni , alors le contexte manquant est supposé ou négligé. Et le consommateur doit décider. Donc, il doit être analysé comme LocalDateTime et converti en OffsetDateTime en ajoutant les informations manquantes:

    • Vous pouvez supposer qu'il s'agit d'une heure UTC. Ajoutez le décalage UTC de 0 heure.
    • Vous pouvez supposer qu'il s'agit d'une heure de l'endroit où la conversion est en cours. Convertissez-le en ajoutant le fuseau horaire du système.
    • Vous pouvez négliger et vous en servir tel quel. C'est utile, par exemple. pour comparer ou soustraire deux fois (voir Duration ), ou lorsque vous ne le savez pas et que cela n'a pas vraiment d'importance (par exemple, les horaires des bus locaux).

Information sur le temps partiel

  • En fonction du contenu de l'horodatage, vous pouvez en extraire LocalDate, LocalTime, OffsetTime, MonthDay, Year ou YearMonth.

Si vous avez les informations complètes, vous pouvez obtenir un Java.time.Instant . Ceci est également utilisé en interne pour convertir entre OffsetDateTime et ZonedDateTime.

Comprendre comment l'analyser

Il existe une documentation complète sur DateTimeFormatter qui permet à la fois d'analyser une chaîne d'horodatage et de la formater en chaîne.

Le DateTimeFormatters pré-créé devrait couvrir tous les formats d'horodatage standard. Par exemple, _ISO_INSTANT_ peut analyser _2011-12-03T10:15:30.123457Z_.

Si vous avez un format spécial, alors vous pouvez créer votre propre DateTimeFormatter (qui est également un analyseur syntaxique).

_private static final DateTimeFormatter TIMESTAMP_PARSER = new DateTimeFormatterBuilder()
   .parseCaseInsensitive()
   .append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SX"))
   .toFormatter();
_

Je recommande de regarder le code source de DateTimeFormatter et de m'inspirer pour en construire un en utilisant DateTimeFormatterBuilder. Pendant que vous y êtes, jetez également un coup d'oeil à ResolverStyle qui contrôle si l'analyseur est LENIENT, SMART ou STRICT pour les formats et les informations ambiguës.

TemporalAccessor

Maintenant, l’erreur la plus fréquente est d’entrer dans la complexité de TemporalAccessor. Cela vient de la façon dont les développeurs étaient habitués à travailler avec SimpleDateFormatter.parse(String). À droite, DateTimeFormatter.parse("...") vous donne TemporalAccessor.

_// No need for this!
TemporalAccessor ta = TIMESTAMP_PARSER.parse("2011-... etc");
_

Mais, avec les connaissances de la section précédente, vous pouvez facilement analyser le type dont vous avez besoin:

_OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z", TIMESTAMP_PARSER);
_

Vous n'avez pas non plus besoin de DateTimeFormatter. Les types que vous souhaitez analyser ont les méthodes parse(String).

_OffsetDateTime myTimestamp = OffsetDateTime.parse("2011-12-03T10:15:30.123457Z");
_

En ce qui concerne TemporalAccessor, vous pouvez l’utiliser si vous avez une vague idée des informations contenues dans la chaîne et que vous souhaitez prendre une décision au moment de l’exécution.

J'espère avoir apporté un peu de compréhension à votre âme :)

Remarque: Il existe un backport de _Java.time_ to Java 6 et 7: ThreeTen-Backport . Pour Android il a ThreeTenABP .

[1] Non seulement ce ne sont pas des rayures, mais il y a aussi des extrêmes étranges. Par exemple, certaines îles voisines du Pacifique ont des fuseaux horaires de +14: 00 et -11: 00. Cela signifie que, même si sur une île, il y a le 1er mai à 15 heures, sur une autre île pas si loin, il est toujours le 30 avril 12 PM (si j'ai bien compté :))

4
Ondra Žižka