Les time zones en Python


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, demander au collègue, au fournisseur, au client, peu 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 !

12 thoughts on “Les time zones en Python

  • Recher

    Bon à savoir, tout ça.

    Sinon, juste pour satisfaire ma curiosité macabre, vous avez le contexte de l’image de l’article ?

    Parce que là je comprends pas bien comment le monsieur en est arrivé là. Si c’est un accident, si c’est intentionnel, quels sont exactement les objets impliqués dans cet événement, etc.

  • Sam Post author

    Nan, c’est pompé d’imposetonanonymat.com, qui est, comme son nom l’indique, repli de trucs anonymes.

  • bob

    @recher
    Tu copies/colles l’url de l’image dans le champ de recherche de «google image», tu pourras tomber sur la source (asiatique manifestement).

  • fab

    Maintenant dès que je me pose une question en python mon réflexe sur google c’est : site:sametmax.com “ma question”. Aujourd’hui c’était les time zones… et je découvre arrow grâce à vous. Voilà, j’suis content :-)

  • said

    Salut,

    petite question avec django.

    Si la timezone de django est en UTC, dois-je paramètrer la timezone de la base de données en UTC aussi ?

    Merci

  • Sam Post author

    Va poser la question sur indexerror.net, ce sera plus approprié.

  • david

    Je pensais être bon en python, car je maîtrisais quelques trucs sympas du style threads …

    Alors que je ne maîtrisais même pas les bases comme les exceptions.

    Aujourd’hui, je me rends compte à quel point je suis une brèle,

    Quand je vois la quantité d’outils à coté je suis passé, et qui m’auraient évité de m’arracher les cheveux.

    En tout cas bravo pour votre site.

  • anon

    Le sujet est un peu vieux, mais au cas où quelqu’un passerait: “peut importe” -> “peu importe”

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.