La gestion du Temps sur une application client/serveur peut sembler simple de prime abord, mais se révélera beaucoup plus complexe au fur et à mesure des usages : timezones multiples, heures récurrentes, heure d'été/d'hiver, traitements cron, leap seconds sont des exemples parmi tant d'autres.
Saviez-vous même que la Commission Européenne envisage d'abolir le passage en heure d'hiver ? Quels impacts cela aurait sur nos applications ?
Mon objectif : qu'à partir de lundi prochain, vous vous posiez les bonnes questions à chaque fois que vous travaillerez sur une date ou une heure dans vos applications.
Pour cela, reprenons les bases du Temps en informatique : composantes d'une date, norme ISO 8601, Timezones et IANA.
Une fois ces bases posées, nous verrons, au travers d'un certain nombre de cas d'utilisation issus de la vraie vie, les bonnes questions qu'il convient de se poser pour mettre le doigt sur les complexités d'implémentation et éviter de tomber dans une faille spatio-temporelle lors du prochain changement d'heure.
Ce talk est accessible à tous et ne se focalisera pas sur un langage / une API en particulier : les concepts prévaudront sur le code.
Cloud Azure – Services de données et bonnes pratiques
Back to Basics - Ne perdez plus votre Temps avec les Dates - JUG SummerCamp Edition
1. Ne perdez plus votre
Temps avec les Dates
Frédéric CAMBLOR
4SH France
@fcamblor
2019-09-13T16:15:00+02:00
#JSC2019🎂 #DateTimeBasics
2. Avertissement
Cette présentation n’aborde pas vraiment des technologies à la mode.
Pire, vous risquez de ressortir de cette présentation avec moins de certitudes que vous
n’en aviez en arrivant.
Acceptez-vous de poursuivre cette présentation sur les Dates et le Temps ?
Si vous voulez savoir comment déployer dans la Blockchain des Microservices Kotlin
codés avec Quarkus sur une plateforme Kubernetes Native dans une image Docker
basée sur GraalVM, il est encore temps de changer de salle :-)
3.
4. Ne perdez plus votre
Temps avec les Dates
Frédéric CAMBLOR
4SH France
@fcamblor
2019-09-13T16:15:00+02:00
#JSC2019🎂 #DateTimeBasics
17. Coordinated Universal Time (UTC)
• Basé sur le TAI
• Agnostic de la géographie / géopolitique
• Stable/Continu dans le temps
Pas d'heure d'été/hiver
• EPOCH : 1970-01-01T00:00:00Z
18. Leap Seconds
• Variable d'ajustement entre UT1& TAI
• Annoncée ~6mois à l'avance
• Positionnée fin juin/décembre
• 37 Leap Seconds depuis l'EPOCH
au 2019-09-13T16:15:00+02:00
29. TIMEZONES: Points clef
• TZ définit des offsets par rapport à UTC
• Ils varient de -12:00 à +14:00
• Les offsets sont "généralement" d'1h
• Offsets variables par timezone (DST)
• TZ Tables : IANA & Windows TimeZones
30. Internet Assigned Numbers Authority (IANA) for Europe
https://github.com/eggert/tz/blob/master/europe
32. En JAVA c'est packagé dans les JRE + TZUpdater tool
http://bit.ly/JRE-IANA
33. Pour MySQL il faut le faire à la main
http://bit.ly/mysql-tzdata
34. Pour NodeJS il faut le gérer à la main
https://www.npmjs.com/package/geo-tz
35. tldr; posez-vous la question !
L'OS peut aussi mettre à disposition ces tables
36. TZ Data: Points clef
• Très lié à la géopolitique
• Les règles changent, régulièrement
• Les règles peuvent changer subitement
• Europe : vers une abolition de DST
• Ne rien considérer comme acquis
⚠ ⚠ ⚠ aux dates dans le futur
37. Lorsque vous stockez une Date+Heure dans le futur :
Stockez l'heure locale & la timezone
de l'utilisateur à l'origine de cette Date
Vous pourrez corriger le problème
(à condition de le détecter)
https://codeblog.jonskeet.uk/2019/03/27/storing-utc-is-not-a-silver-bullet/
38.
39. Daylight Saving Time (DST)
tzDate("2019-03-31T01:10:00", "Europe/Paris")
=> 2019-03-31T01:10:00+01:00
Certaines heures locales n'existent pas (Local Time Gap)
02:00 -> 03:00 pour Europe/Paris
tzDate("2019-03-31T01:10:00", "Europe/Paris").add(1, "hour")
=> 2019-03-31T03:10:00+02:00
tzDate("2019-03-31T02:10:00", "Europe/Paris")
=> 2019-03-31T03:10:00+02:00
40. Daylight Saving Time (DST)
tzDate("2019-10-27T02:10:00", "Europe/Paris")
=> 2019-10-27T02:10:00+02:00
Certaines heures locales existent 2 fois
02:00 -> 03:00 pour Europe/Paris
tzDate("2019-10-27T02:10:00", "Europe/Paris").add(1, "hour")
=> 2019-10-27T02:10:00+01:00
41. La date/heure DST varie en fonction de la timezone
http://bit.ly/dst-diffs
42. Fausse bonne idée
"Notre batch de facturation passe tous les jours à
2h30 du matin (heure locale), car il s'agit du moment
où le système est le moins sollicité"
🤦 '
48. 🤔
Qui a raison ?
public ZonedDateTime calculateNextThingAfter(ZonedDateTime d) {
return d.plusDays(1);
}
public ZonedDateTime calculateNextThingAfter(ZonedDateTime d) {
return d.plusHours(24);
}
#scaleMatters
49. Fun fact : aucun 2011-12-30 (Vendredi) aux Samoa
https://www.timeanddate.com/news/time/samoa-dateline.html
54. Format non ISO => date Locale | Format ISO => date UTC
https://twitter.com/seldo/status/1091861205260500992
55. D'autres blagues
• Les mois commencent à 0
On a trouvé d'où vient le "Java" de "Javascript" !
• Aucune gestion des timezones / internationalisation
• La gestion des "anciens" timezone offsets est (complètement) broken
... chez tout le monde (Chrome, Firefox, IE, Safari, Node)
https://codeofmatt.com/javascript-date-type-is-
59. Le premier jour de la semaine peut varier...
https://en.wikipedia.org/wiki/Week
60. La notion de Chronology
• ISO 8601 = Calendrier Grégorien de facto
• Il en existe plein d'autres
Julien, Ethiopien, Bouddhiste, Islamiste, Copte...
• Règles différentes sur l'année de départ
• Règles différentes sur les années bissextiles
• Répartition différente des jours dans les mois
62. Vos serveurs doivent être en UTC
•Timezone de l'OS
•Timezone de la DB
Il n'est pas tout le temps possible de la changer
•Timezone de votre serveur d'App
sudo timedatectl set-timezone UTC
# Or : sudo dpkg-reconfigure tzdata
-Duser.timezone=UTC
63. L'intervalle de temps implicite
👍 GET /messages?after=2019-02-09T00:00:00+01:00&before=2019-02-09T23:59:59+01:00
👎 GET /messages?date=2019-02-09
👋 GET /messages?date=2019-02-09&tz=Europe/Paris
64. Date search Patterns
• Dans 99% des cas vous allez faire des recherches
sur des intervalles de temps
• Faites (si possible) les conversions coté appelant* (client)
• Stockez* & Recherchez vos datetime en UTC
65. Attention aux"Heures locales"
"Le magasin ouvre à 9h du matin les jours ouvrés"
"J'aimerais planifier une réunion à 15h (Europe/Paris) tous les lundi"
Heure sans Date attachée
66. [On est le 08/03 à Bordeaux]
"J'aimerais planifier une réunion à 15h (Europe/Paris) tous les lundi"
Attention aux"Heures locales"
1/ "Le dernier lundi d'avril"
=> 2019-04-29 (Date Locale)
2/ Ajout de la composante heure + timezone offset
=> 2019-04-29T15:00:00+02:00
Attention aux cas exceptionnels !
👍 "localTime": "15:00 [Europe/Paris]"
3/ Décalage dans une tz cible (ex: America/New_York)
=> 2019-04-29T09:00:00-04:00
67. Time-only Patterns
• Stocker la Timezone de l'utilisateur ayant saisi l'heure
• Évitez de stocker votre Time dans un DateTime
🙈 1970-01-01T15:35:00Z
• Mettez à disposition la Timezone cible
68. Date-only Patterns
• Utilisez une structure de données adhoc
• Évitez de stocker votre Date dans un DateTime
🙈 1732-02-22T00:00:00Z
• Si vous êtes sûr de la Timezone, stockez-la
69. Les"Local Datetime"
Une DateTime sans Timezone ne peut pas être
"identifiée" chronologiquement
À éviter, absolument !
http://blog.schauderhaft.de/2018/03/14/dont-use-localdatetime/