2017-10-19 23 views
3

Je ne souhaite pas utiliser la bibliothèque pytz car le projet sur lequel je travaille nécessite des documents pour introduire des dépendances. Si je peux y arriver sans une bibliothèque tierce, je serai plus heureux.Comment convertir un objet datetime entre les fuseaux horaires CEST et UTC

Je rencontre des problèmes lors de la conversion d'une date entre CET et UTC lorsque la date est en heure d'été. Il est une heure différente de ce que je pense:

>>> print from_cet_to_utc(year=2017, month=7, day=24, hour=10, minute=30) 
2017-07-24T09:30Z 

2017-07-24T08:30Z # expected 

CET est une heure d'avance sur UTC et en été est de 2 heures à l'avance. Donc, je m'attendais à 10h30 en Europe centrale en plein été à 8h30 UTC.

La fonction est:

from datetime import datetime, tzinfo, timedelta 

def from_cet_to_utc(year, month, day, hour, minute): 
    cet = datetime(year, month, day, hour, minute, tzinfo=CET()) 
    utc = cet.astimezone(tz=UTC()) 
    return '{:%Y-%m-%d:T%H:%MZ}'.format(utc) 

J'utilise deux informations fuseau horaire objets:

class CET(tzinfo): 
    def utcoffset(self, dt): 
     return timedelta(hours=1) 

    def dst(self, dt): 
     return timedelta(hours=2) 

class UTC(tzinfo): 
    def utcoffset(self, dt): 
     return timedelta(0) 

    def dst(self, dt): 
     return timedelta(0) 

Répondre

3

Juste en complément de @Sergey's answer. Pour savoir si vous devez appliquer l'heure d'été ou non, vous devez obtenir les données historiques de la date/heure de début et de fin de l'heure d'été et vérifier si elle doit être appliquée ou non au datetime spécifié. Vous pouvez obtenir ceux de IANA database ou consulter dans de nombreuses sources en ligne, telles que timeanddate website.

Un autre détail est que CET est un ambiguous name, parce qu'il est utilisé by more than one timezone. Les noms des fuseaux horaires réels utilisent the ones defined by IANA database (toujours au format Region/City, comme Europe/Paris ou Europe/Berlin).


vides et les dédoublements

Je vais utiliser Europe/Berlin timezone comme exemple. En cette année (2017), DST à Berlin a commencé en Mars 26 : à 2 heures du matin, les horloges ont décalé 1 heure avant à 3 heures du matin, et l'offset a changé de +01:00 à +02:00.

Vous pouvez également penser que les horloges ont sauté directement de 01h59 à 03h00, ce qui signifie que toutes les heures locales entre 02h00 et 02h59 n'existaient pas ce jour-là, à ce fuseau horaire. C'est ce qu'on appelle un écart, et vous devriez vérifier cette situation (une approche courante consiste à ajuster 2 AM à l'heure valide suivante - dans ce cas, 3 heures du matin).

Dans le même fuseau horaire, en 2017, l'heure d'été prendra fin le 29 Octobre e: à 3 heures, les horloges changeront arrière 1 heure à 2 heures du matin, et le décalage changera +02:00-+01:00. Cela signifie que toutes les heures locales entre 2 heures du matin et 2h59 du matin existeront deux fois: une fois dans DST (+02:00) et une fois dans non DST (+01:00). C'est ce qu'on appelle un chevauchement, et lors de la création d'un datetime qui tombe dans ce cas, vous devez choisir celui que vous choisirez de créer (devrait-il être 2 heures du matin en DST ou non DST? Vous décidez!).


PS: Chaque pays européen qui utilise CET elle a adopté aujourd'hui une autre année, donc si vous avez affaire à de vieilles dates vous devez considérer aussi. Un autre détail est que DST est défini par les gouvernements, et il n'y a aucune garantie que les règles seront comme ça pour toujours, et vous devrez mettre à jour les règles en conséquence - un autre avantage de l'utilisation pytz, que ses mises à jour sont générées à partir de IANA libère et published in PyPI (merci @Matt Johnson pour pointing this info). Etes-vous sûr que la paperasserie est pire que de coder ces règles à la main? Vérifiez simplement how to read IANA tz files et peut-être que vous allez changer d'avis.

+2

Juste pour répondre à votre question sur les mises à jour de pytz, elles sont générées à partir des versions de l'IANA. IANA 2017b => pytz 2017.2. Stuart Bishop les maintient, et les publie à pypi ici: https://pypi.python.org/pypi/pytz#downloads –

+0

@MattJohnson J'ai mis à jour la réponse avec cette info, merci beaucoup! –

+1

Vos réponses et celles de Sergey ont été très utiles. Je pense que vous avez fait le plus pour me convaincre que je devrais laisser le soin aux experts. Notre solution consiste à utiliser l'heure UTC comme entrée, puis aucune conversion n'est requise de notre part (c: –

3

Vous devriez lire attentivement le tzinfo manual, comme il y est expliqué.

D'abord, dst() devrait retourner en général un timedelta(0) ou timedelta(hours=1), jamais 2 heures ou plus, et rarement quelque chose entre les deux (par exemple 30 minutes). Ceci est seulement un décalage dst par rapport au décalage TZ normal, pas le décalage TZ complet dans le mode dst.

Deuxièmement, utcoffset() doit déjà inclure la correction dst. La méthode dst() seule est également utilisée dans certains cas décrits dans le manuel (par exemple pour la détection si dst est active ou non), mais elle n'est pas automatiquement appliquée au décalage TZ à moins que vous ne le fassiez.

Votre code devrait ressembler à ceci:

from datetime import datetime, tzinfo, timedelta 

class CET(tzinfo): 
    def utcoffset(self, dt): 
     return timedelta(hours=1) + self.dst(dt) 

    def dst(self, dt): 
     dston = datetime(year=dt.year, month=3, day=20) 
     dstoff = datetime(year=dt.year, month=10, day=20) 
     if dston <= dt.replace(tzinfo=None) < dstoff: 
      return timedelta(hours=1) 
     else: 
      return timedelta(0) 

class UTC(tzinfo): 
    def utcoffset(self, dt): 
     return timedelta(0) 

    def dst(self, dt): 
     return timedelta(0) 

def from_cet_to_utc(year, month, day, hour, minute): 
    cet = datetime(year, month, day, hour, minute, tzinfo=CET()) 
    utc = cet.astimezone(tz=UTC()) 
    return '{:%Y-%m-%d:T%H:%MZ}'.format(utc) 


print from_cet_to_utc(year=2017, month=7, day=24, hour=10, minute=30) 
# 2017-07-24:T08:30Z 

Ici, je suppose courage que la DST est en vigueur 20.03.YYYY - 19.10.YYYY inclusif, où AAAA est l'année de l'objet Date/datetime.

Habituellement, cette logique de la & hors dates est beaucoup plus compliquée: les changements se produisent le dimanche soir seulement, parfois le dernier dimanche du mois, et varient entre les années, parfois entre la souveraineté des États (avec des limites qui changent également au fil des ans), et avec les lois de l'heure d'été et le changement de fuseau horaire changeant toutes les quelques années (bonjour, la Russie).

+3

En fait, vous devriez également vérifier l'heure du jour - en Europe centrale, les changements d'heure d'été se produisent généralement entre 2 heures du matin et 3 heures du matin, donc vérifier juste la date n'est pas assez précise. Quoi qu'il en soit, je vais upvote dès que le jour change (j'ai atteint ma limite de vote pour aujourd'hui). –

+0

Actuellement, 2h00 au printemps, 3h00 à l'automne. https://www.timeanddate.com/time/change/france/paris –

+1

@Hugo Vous avez raison. Merci de compléter ma réponse. Cependant, même ce que vous avez décrit n'est qu'une partie de l'iceberg. J'ai volontairement répondu à la question uniquement concernant les parties python et 'datetime.tzinfo', ce qui semblait être un problème pour l'auteur de la question. Bien que mentionné que le calcul de tz réel est un sujet très difficile et vaste. Parce qu'il est si grand, que l'on peut écrire un livre purement sur le bon travail avec les dates et les heures (pas une blague). –