Analyser une chaîne avec la date et l'heure dans un moment particulier (Java l'appelle un " Instant
") est assez compliqué. Java a résolu ce problème en plusieurs itérations. Le dernier, java.time
et java.time.chrono
, couvre presque tous les besoins (sauf Time Dilation :)).
Cependant, cette complexité apporte beaucoup de confusion.
La clé pour comprendre l'analyse de date est:
Pourquoi Java a-t-il tant de façons d'analyser une date
- 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 respectifs. Ensuite, il y a par exemple l'horodatage UNIX. Heureusement, tout le monde (des affaires) a réussi à utiliser la même chose.
- Historiquement, les systèmes étaient passés de / à, pour diverses raisons . Par exemple, du calendrier julien au calendrier grégorien en 1582. Les dates «occidentales» antérieures doivent donc être traitées différemment.
- Et bien sûr, le changement ne s'est pas produit tout de suite. Parce que le calendrier provenait des quartiers généraux de certaines religions et d'autres parties de l'Europe croyaient en d'autres dieux, par exemple l'Allemagne n'a pas changé avant l'année 1700.
... et pourquoi le LocalDateTime
, ZonedDateTime
et al. si compliqué
Il y a fuseaux horaires . Un fuseau horaire est fondamentalement une "bande" * [1] de la surface de la Terre dont les autorités suivent les mêmes règles de quand a-t-elle quel décalage horaire. Cela inclut les règles de l'heure d'été.
Les fuseaux horaires changent au fil du temps pour divers domaines, principalement en fonction de qui conquiert qui. Et les règles d'un fuseau horaire changent également au fil du temps .
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 par exemple "Prague", mais qui a un décalage d'heure d'été et un décalage d'heure d'hiver.
Si vous obtenez un horodatage avec un fuseau horaire, le décalage peut varier, selon la partie de l'année dans laquelle il se trouve. Pendant l'heure bissextile, l'horodatage peut signifier 2 heures différentes, donc sans informations supplémentaires, il ne peut pas être fiable converti.
Remarque: Par horodatage, je veux dire "une chaîne qui contient une date et / ou une heure, éventuellement avec un fuseau horaire et / ou un décalage horaire".
Plusieurs fuseaux horaires peuvent partager le même décalage horaire pour certaines périodes. Par exemple, le fuseau horaire GMT / UTC est le même que le fuseau horaire "Londres" lorsque le décalage horaire d'été n'est pas en vigueur.
Pour le rendre un peu plus compliqué (mais ce n'est pas trop important pour votre cas d'utilisation):
- Les scientifiques observent la dynamique de la Terre, qui change avec le temps; sur cette base, ils ajoutent des secondes à la fin des années individuelles. (Alors
2040-12-31 24:00:00
peut s'agir d'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, y compris ces nouvelles données.
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 compare puisse donner des résultats différents lors de l'exécution sur différentes versions du logiciel. Cela s'applique également à la comparaison entre le fuseau horaire affecté et un autre fuseau horaire.
Si cela provoque un bogue dans votre logiciel, envisagez d'utiliser un horodatage qui n'a pas de règles aussi compliquées, comme l' horodatage UNIX .
À cause 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 de l'analyse future.
Les API de JDK pour cela ont évolué avec les besoins contemporains
- Les premières versions de Java venaient
java.util.Date
une approche un peu naïve, en supposant qu'il n'y avait que l'année, le mois, le jour et l'heure. Cela n'a pas suffi rapidement.
- De plus, les besoins des bases de données étaient différents, donc assez tôt,
java.sql.Date
été introduit, avec ses propres limites.
- Parce que ni l'un ni l'autre ne couvrait bien différents calendriers et fuseaux horaires, l'
Calendar
API a été introduite.
- Cela ne couvrait toujours pas la complexité des fuseaux horaires. Et pourtant, le mélange des API ci-dessus était vraiment difficile à travailler. Alors que les développeurs Java ont commencé à travailler sur des applications Web mondiales, les bibliothèques qui ciblaient la plupart des cas d'utilisation, comme JodaTime, sont rapidement devenues populaires. JodaTime était la norme de facto depuis environ une décennie.
- Mais le JDK ne s'est pas intégré à JodaTime, donc travailler avec lui était un peu lourd. Ainsi, après une très longue discussion sur la façon d'aborder la question, JSR-310 a été créé principalement sur la base de JodaTime .
Comment y faire face dans Java java.time
Déterminer à quel type analyser un horodatage
Lorsque vous consommez 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 comme "Impossible de créer instantanément" ou "Décalage de zone manquant" ou "ID de zone inconnue" etc.
Contient-il la date et l'heure?
At-il un décalage horaire?
Un décalage horaire est la +hh:mm
pièce. Parfois, il +00:00
peut être remplacé par Z
le «temps zoulou», UTC
le temps universel coordonné ou GMT
le temps moyen de Greenwich. Celles-ci définissent également le fuseau horaire.
Pour ces horodatages, vous utilisez OffsetDateTime
.
At-il un fuseau horaire?
Pour ces horodatages, vous utilisez ZonedDateTime
.
La zone est spécifiée soit par
- nom ("Prague", "Pacific Standard Time", "PST"), ou
- "zone ID" ("America / Los_Angeles", "Europe / London"), représenté par java.time.ZoneId .
La liste des fuseaux horaires est compilée par une "base de données TZ" , soutenue par l'ICAAN.
Selon ZoneId
javadoc, les identifiants de zone peuvent également être spécifiés en quelque sorte Z
et offset. Je ne sais pas comment cela correspond aux zones réelles. Si l'horodatage, qui n'a qu'un TZ, tombe dans une heure bissextile de décalage horaire, alors il est ambigu et l'interprétation est sujette ResolverStyle
, voir ci-dessous.
S'il n'a ni l'un ni l'autre , alors le contexte manquant est supposé ou négligé. Et le consommateur doit décider. Il doit donc être analysé 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 que c'est un moment de l'endroit où la conversion se produit. Convertissez-le en ajoutant le fuseau horaire du système.
- Vous pouvez négliger et simplement l'utiliser tel quel. Cela est utile, par exemple, pour comparer ou soustraire deux fois (voir
Duration
), ou lorsque vous ne savez pas et que cela n'a pas vraiment d'importance (par exemple, les horaires des bus locaux).
Informations sur le temps partiel
- D' après ce que l'horodatage contient, vous pouvez prendre
LocalDate
, LocalTime
, OffsetTime
, MonthDay
, Year
ou YearMonth
hors de celui - ci.
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
.
Découvrez comment l'analyser
Il existe une documentation complète sur DateTimeFormatter
laquelle peut à la fois analyser une chaîne d'horodatage et formater en chaîne.
Les créé pré- DateTimeFormatter
s devraient couvrir plusmoins 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, vous pouvez créer votre propre DateTimeFormatter (qui est également un analyseur).
private static final DateTimeFormatter TIMESTAMP_PARSER = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SX"))
.toFormatter();
Je vous recommande de regarder le code source de DateTimeFormatter
et de vous inspirer sur la façon d'en créer un en utilisant DateTimeFormatterBuilder
. Pendant que vous y êtes, regardez également ResolverStyle
qui contrôle si l'analyseur est LENIENT, SMART ou STRICT pour les formats et les informations ambiguës.
TemporalAccessor
Maintenant, l'erreur fréquente est d'entrer dans la complexité de TemporalAccessor
. Cela vient de la façon dont les développeurs étaient habitués à travailler SimpleDateFormatter.parse(String)
. D'accord, DateTimeFormatter.parse("...")
vous donne TemporalAccessor
.
// No need for this!
TemporalAccessor ta = TIMESTAMP_PARSER.parse("2011-... etc");
Mais, équipé des 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 en fait pas besoin DateTimeFormatter
non plus. Les types que vous souhaitez analyser ont les parse(String)
méthodes.
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 souhaitez décider au moment de l'exécution.
J'espère avoir jeté une lumière de compréhension sur votre âme :)
Remarque: il existe un backport de java.time
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 +14: 00 et -11: 00. Cela signifie que si sur une île, il y a le 1er mai 15h, sur une autre île pas si loin, c'est toujours le 30 avril 12h (si j'ai bien compté :))
ZonedDateTime
plutôt qu'unLocalDateTime
. Le nom est contre-intuitif; lesLocal
moyens toute localité en général plutôt qu'une zone de temps spécifique. En tant que tel, unLocalDateTime
objet n'est pas lié à la chronologie. Pour avoir un sens, pour obtenir un moment précis sur la ligne de temps, vous devez appliquer un fuseau horaire.