Sam & Max » dateutil http://sametmax.com Du code, du cul Sat, 07 Nov 2015 10:56:13 +0000 en-US hourly 1 http://wordpress.org/?v=4.1 Les time zones en Python 6 http://sametmax.com/les-time-zones-en-python/ http://sametmax.com/les-time-zones-en-python/#comments Tue, 15 Oct 2013 18:23:38 +0000 http://sametmax.com/?p=7448 arrow, une lib Python qui résout le problème]]> Si vous suivez un peu le blog, vous savez comment manipuler les dates en Python. Et c’est trop cool. Parce que le temps, c’est quelque chose de difficile en programmation, comme la gestion des monnaies ou la traduction d’une interface.

Mais notre article ne traite pas des time zones, c’est à dire de la gestion des fuseaux horaires. La raison est très simple, Sam est une grosse feignasse, et comme c’est un sujet super galère, il l’a soigneusement évité, vous renvoyant à l’usage de pytz. Démerdez vous.

Qu’est-ce qui a changé ? Le sujet est-il devenu plus simple ? Sam a-t-il un regain de motivation ? Le fait de parler de lui à la 3eme personne est-il signe de sénilité ?

Bien sûr que non, c’est simplement que je suis tombé sur arrow, une lib Python qui résout le problème. Pour faire simple, c’est presque exactement la lib que j’avais commencé à coder pour la gestion du temps. Sauf que le mec, lui, il l’a terminé, alors que la mienne, elle est au fin fond d’un repo git poussiéreux depuis 8 mois.

Bref, laissez tout tomber, n’installez plus dateutil, pytz et autres libs que j’ai pu vous recommander par le passé (elle sont incluses et abstraites par arrow). Faites juste :

pip install arrow

Tout commence par l’UTC

Comme pour la gestion des encodings, la gestion des dates se fait en utilisant une base commune. Pour l’encoding, c’est unicode, pour les dates, c’est UTC.

Donc, si vous prévoyez de gérer différents fuseaux horaires dans votre programme, TOUT votre programme – j’ai dit TOUT – doit manipuler exclusivement des dates en UTC.

Comme avec l’encoding, l’idée est la suivante :

  • A l’entrée de votre programme, vous acceptez des dates avec une timezone. Vous convertissez ces dates vers l’UTC.
  • Dans votre programme, vous manipuler uniquement de l’UTC.
  • A la sortie du programme, vous renvoyez des dates dans une timezone.

Ceci suppose donc que :

  • Vous savez ce qu’est une entrée et une sortie. Je vous fait un rappel : tout ce qui est pas dans votre programme (query base de données, socket, fichiers, print, input utilisateur, clic, affichage, etc.).
  • Vous DEVEZ savoir la timezone de ce qui rentre. Si vous ne le savez pas, vous ne pouvez absolument rien faire à part afficher à votre utilisateur que vous ne savez pas et le réglage par défaut . Pour la sortie, si vous ne savez pas, vous pouvez sortir de l’UTC par défaut, ça mange pas de pain. Mais pour l’entrée, il faut lire la spec, la doc, demandé au collègue, au fournisseur, au client, peut importe, il faut trouver l’info.

Avec arrow, c’est simplissime…

Si vous créez une date vous même, il suffit de toujours le faire en UTC :

>>> import arrow
>>> maintenant = arrow.utcnow() # heure et date actuelle en UTC
>>> print(maintenant)
2013-10-15T17:15:19.139000+00:00
>>> from datetime import datetime
>>> date_de_sortie_des_goonies = arrow.get(datetime(1985, 12, 4), 'US/Pacific')
>>> print(date_de_sortie_des_goonies)
1985-12-04T00:00:00-08:00
>>> date_de_sortie_des_goonies = date_de_sortie_des_goonies.to('utc')
>>> print(date_de_sortie_des_goonies)
1985-12-04T08:00:00+00:00, on a des dates uniquements en UTC.

Si vous importez une date, c’est très simple également :

>>> arrow.get(1367900664) # un timestamp marche..
<Arrow [2013-05-07T04:24:24+00:00]>
>>> arrow.get('1367900664') # ... même en string
<Arrow [2013-05-07T04:24:24+00:00]>
>>> arrow.get(1367900664.152325) # ... ou en float
<Arrow [2013-05-07T04:24:24.152325+00:00]>
>>> arrow.get('2013-09-30T15:34:00.000-07:00').to('utc') # parser une date standard...
<Arrow [2013-09-30T22:34:00+00:00]>
>>> from dateutil import tz # pour parser une date custo
>>> arrow.Arrow.strptime('2013-05-05 12:30:45', '%Y-%m-%d %H:%M:%S', tz.gettz('Paris/Europe'))
<Arrow [2013-05-05T12:30:45+00:00]>

Tout ça renvoie de l’UTC.

Et quand vous voulez afficher une date, vous pouvez tout simplement la reconvertir vers la timezone de votre choix avec la méthode to() et la formater comme avec format()

Stockage des dates

Quand on stocke une date (dans une base de données, un fichier, etc), il faut se demander quel est le but du stockage. Si le but est de stocker la date d’un événement, mettez la date en UTC. Un timestamp suffit, ou alors la représentation YYYY-MM-DD hh:mm:ss. Par contre, si vous voulez stocker une date liée à un événement et un lieu (par exemple un rendez-vous d’affaire ou le décollage d’un avion), stockez la date en UTC avec sa timezone dans un champ à côté, sinon vous serez bien baisé quand vous voudrez la ressortir.

Ah oui, et réglez toujours tous vos serveurs sur UTC. Toujours.

En bonus

On peut obtenir toutes les infos utiles sans tourner autour du pot :

>>> a = arrow.utcnow()
>>> print(a.datetime)
2013-10-15 17:48:28.335000+00:00
>>> print(a.timestamp)
1381859308
>>> print(repr(a.naive)) # on accès à l'objet datetime au cas où...
datetime.datetime(2013, 10, 15, 17, 49, 25, 515000)
>>> print(a.tzinfo) # ... et à l'objet tzinfo
tzutc()
>>> print(a.year, a.hour)
(2013, 17)

Et on peut faire des replacements et des calculs flous, comme avec dateutil :

>>> a = arrow.utcnow()
>>> print(a.format('DD, MMMM', locale='fr_FR'))
15, Octobre
>>> a = a.replace(day=30)
>>> print(a.format('DD, MMMM', locale='fr_FR'))
30, Octobre
>>> a = a.replace(days=+1)
>>> print(a.format('DD, MMMM', locale='fr_FR'))
31, Octobre
>>> a = a.replace(months=-1)
>>> print(a.format('DD, MMMM', locale='fr_FR'))
30, Septembre

Faire de l’affichage relatif :

>>> before = arrow.utcnow().replace(hours=-3)
>>> before.humanize(locale='Fr_fr'))
'il y a 3 heures'

Bref, arrow, c’est de la bombe baby !

]]>
http://sametmax.com/les-time-zones-en-python/feed/ 6