Pourquoi datetime.datetime.utcnow () ne contient-il pas d'informations de fuseau horaire?

285
datetime.datetime.utcnow()

Pourquoi cela n'a-t-il datetimepas d'informations de fuseau horaire étant donné qu'il s'agit explicitement d'un UTC datetime?

Je m'attendrais à ce que cela contienne tzinfo.

Vitaly Babiy
la source
Comment convertir un champ de date au format iso normal qui est de type chaîne au format utc?
Navi

Réponses:

192

Cela signifie qu'il est naïf de fuseau horaire, vous ne pouvez donc pas l'utiliser avec datetime.astimezone

vous pouvez lui donner un fuseau horaire comme celui-ci

import pytz  # 3rd party: $ pip install pytz

u = datetime.utcnow()
u = u.replace(tzinfo=pytz.utc) #NOTE: it works only with a fixed utc offset

maintenant vous pouvez changer les fuseaux horaires

print(u.astimezone(pytz.timezone("America/New_York")))

Pour obtenir l'heure actuelle dans un fuseau horaire donné, vous pouvez passer datetime.now()directement à tzinfo :

#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz

print(datetime.now(pytz.timezone("America/New_York")))

Il fonctionne pour n'importe quel fuseau horaire, y compris ceux qui observent l'heure d'été (DST), c'est-à-dire qu'il fonctionne pour les fuseaux horaires qui peuvent avoir différents décalages utc à différents moments (décalage utc non fixe). Ne pas utiliser tz.localize(datetime.now())- il peut échouer pendant la transition de fin de l'heure d'été lorsque l'heure locale est ambiguë.

John La Rooy
la source
216
Mais il n'y a aucune bonne raison pour qu'il soit naïf de fuseau horaire - il est spécifié qu'il est UTC. Pourquoi avez-vous besoin de rechercher une bibliothèque tierce pour la faire fonctionner correctement?
Mark Ransom
4
Je suis d'accord; pour moi, les temps «naïfs» sont complètement inutiles. Il y a actuellement une discussion sur la liste python sur l'ajout de pytz au stdlib; le problème n'est pas l'octroi de licences mais le fait que les données de fuseau horaire sont mises à jour si souvent (ce que Python lui-même ne peut pas être). De plus, pytz n'implémente pas l'interface tzinfo de la manière attendue, vous pouvez donc obtenir des erreurs si vous essayez d'utiliser certains des fuseaux horaires de la ville astimezone. Ainsi, datetime n'a pas seulement de fuseaux horaires natifs, mais la seule implémentation largement disponible de tzinfo n'est pas conforme à la norme supposée.
bobince
5
@bobince Pourquoi pytz et les bibliothèques de datetime standard ne fonctionnent-ils pas pour vous? Le noyau Python et pytz évoluant en tant que projets indépendants réduit la complexité logistique pour l'équipe principale. Oui, la réduction de la complexité pour l'équipe principale de Python augmente la complexité pour tous les utilisateurs de Python qui ont besoin de traiter les fuseaux horaires mais, j'espère qu'ils ont pris cette décision pour une bonne raison. La règle "La bibliothèque standard n'a pas d'instances tzinfo ..." est géniale car c'est simple, pourquoi faire une exception ici?
Derek Litz
15
Que diriez-vous justeu=datetime.now(pytz.utc)
Craig McQueen
4
@bain: ne pas utiliser tz.localize(datetime.now()); utiliser à la datetime.now(tz)place.
jfs
142

Notez que pour Python 3.2 et ultérieur, le datetimemodule contient datetime.timezone. La documentation de datetime.utcnow()dit:

Un datetime UTC courant peut être obtenu en appelant .datetime.now(timezone.utc)

Vous pouvez donc faire:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2014, 7, 10, 2, 43, 55, 230107, tzinfo=datetime.timezone.utc)
Craig McQueen
la source
2
Lequel est préféré? datetime.now(timezone.utc)ou datetime.utcnow(timezone.utc)?
Jesse Webb
8
datetime.utcnow()ne prend aucun argument. Il faudrait donc que ce soit le cas datetime.now(timezone.utc).
Craig McQueen
1
datetime.now()renverra l'heure de la machine mais datetime.utcnow()renverra l'heure UTC réelle.
Babu
13
@Babu: datetime.utcnow()ne signifie pas tzinfoqu'il s'agit de l'UTC. Mais datetime.now(datetime.timezone.utc)retourne l'heure UTC avec tzinfo set.
Craig McQueen
@CraigMcQueen Donc, si nous passons un tzobjet dans le constructeur now, il retournera l'heure de ce fuseau horaire? D'accord! Merci d'avoir souligné.
Babu
71

Les bibliothèques Python standard n'incluent aucune classe tzinfo (mais voir pep 431 ). Je ne peux que deviner les raisons. Personnellement, je pense que c'était une erreur de ne pas inclure une classe tzinfo pour UTC, car celle-ci est suffisamment controversée pour avoir une implémentation standard.

Edit: Bien qu'il n'y ait pas d'implémentation dans la bibliothèque, il y en a une donnée comme exemple dans la tzinfodocumentation .

from datetime import timedelta, tzinfo

ZERO = timedelta(0)

# A UTC class.

class UTC(tzinfo):
    """UTC"""

    def utcoffset(self, dt):
        return ZERO

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return ZERO

utc = UTC()

Pour l'utiliser, pour obtenir l'heure actuelle en tant qu'objet datetime conscient:

from datetime import datetime 

now = datetime.now(utc)

Il existe datetime.timezone.utcen Python 3.2+:

from datetime import datetime, timezone 

now = datetime.now(timezone.utc)
Mark Ransom
la source
8
Allez comprendre pourquoi cette classe n'a pas été fournie en premier lieu (et, plus important encore, utilisée pour des datetimeobjets créés par utcnow()) ...
André Caron
17
L'objet fuseau horaire timezone.utca finalement été ajouté à Python 3.2. Pour des raisons de compatibilité descendante, utcnow()renvoie toujours un objet temporel sans fuseau horaire, mais vous pouvez obtenir ce que vous voulez en appelant now(timezone.utc).
mhsmith
4
@rgove, c'est le genre de correction des torts qui était censé être un jeu équitable pour Python 3. Ils ne devraient pas s'inquiéter de la compatibilité descendante. Il y a un autre exemple que j'ai lu au cours des derniers jours - le structmodule ferait des conversions automatiques d'Unicode en bytestring, et la décision finale a été de rompre la compatibilité avec les versions antérieures de Python 3 pour empêcher une mauvaise décision d'aller de l'avant.
Mark Ransom
2
Je suis stupéfait que la tzinfodocumentation de Python inclut des exemples de code pour l'implémenter, mais ils n'incluent pas cette fonctionnalité dans datetime lui-même! docs.python.org/2/library/datetime.html#datetime.tzinfo.fromutc
LS
1
@LS oui, pytzest une excellente ressource. Au moment où j'avais édité ma réponse pour mettre l'exemple de code, quelqu'un d'autre l'avait déjà suggéré et je ne voulais pas voler leur tonnerre.
Mark Ransom
20

Le pytzmodule est une option, et il y en a une autre python-dateutilqui, bien qu'il s'agisse également d'un package tiers, peut déjà être disponible en fonction de vos autres dépendances et de votre système d'exploitation.

Je voulais juste inclure cette méthodologie pour référence - si vous l'avez déjà installée python-dateutilà d'autres fins, vous pouvez l'utiliser tzinfoau lieu de la dupliquer avecpytz

import datetime
import dateutil.tz

# Get the UTC time with datetime.now:
utcdt = datetime.datetime.now(dateutil.tz.tzutc())

# Get the UTC time with datetime.utcnow:
utcdt = datetime.datetime.utcnow()
utcdt = utcdt.replace(tzinfo=dateutil.tz.tzutc())

# For fun- get the local time
localdt = datetime.datetime.now(dateutil.tz.tzlocal())

J'ai tendance à convenir que les appels à utcnowdoivent inclure les informations de fuseau horaire UTC. Je soupçonne que cela n'est pas inclus car la bibliothèque native datetime utilise par défaut des datetimes naïfs pour la compatibilité croisée.

bbengfort
la source
1
NameError: le nom 'dt' n'est pas défini
xApple
J'utilisais l'appel datetime.datetime.utcfromtimestamp () et j'avais besoin d'ajouter tzinfo, la deuxième solution a fonctionné pour moi: utcdt = datetime.datetime.utcfromtimestamp(1234567890).replace(dateutil.tz.tzutc())
Ian Lee
1
note: contrairement à datetime.now(pytz_tz)cela fonctionne toujours; datetime.now(dateutil.tz.tzlocal())peut échouer pendant les transitions DST . PEP 495 - La désambiguïsation de l'heure locale pourrait améliorer la dateutilsituation à l'avenir.
jfs
@IanLee: vous pouvez utiliser utc_dt = datetime.fromtimestamp(1234567890, dateutil.tz.tzutc())(remarque: dateutilavec un décalage utc non fixe (tel que dateutil.tz.tzlocal()) peut échouer ici , utilisez plutôt une pytzsolution basée sur ).
jfs
Comme mon programme importait déjà dateutilpour dateutil.parser, j'ai préféré cette solution. Il était aussi simple que: utcCurrentTime = datetime.datetime.now(tz=dateutil.tz.tzutc()). Alto!!
LS
11

Julien Danjou a écrit un bon article expliquant pourquoi vous ne devriez jamais traiter les fuseaux horaires . Un extrait:

En effet, l'API datetime Python renvoie toujours des objets datetime ignorants, ce qui est très regrettable. En effet, dès que vous obtenez un de ces objets, il n'y a aucun moyen de savoir quel est le fuseau horaire, donc ces objets sont assez "inutiles" à eux seuls.

Hélas, même si vous pouvez utiliser utcnow(), vous ne verrez toujours pas les informations de fuseau horaire, comme vous l'avez découvert.

Recommandations:

  • Utilisez toujours des datetimeobjets conscients , c'est-à-dire avec des informations de fuseau horaire. Cela garantit que vous pouvez les comparer directement (les datetime objets avertis et ignorants ne sont pas comparables) et les rendra correctement aux utilisateurs. Effet de levier pytz d'avoir des objets de fuseau horaire.

  • Utilisez ISO 8601 comme format de chaîne d'entrée et de sortie. Permet datetime.datetime.isoformat()de renvoyer des horodatages sous forme de chaîne formatée à l'aide de ce format, qui inclut les informations de fuseau horaire.

  • Si vous devez analyser des chaînes contenant des horodatages au format ISO 8601, vous pouvez vous en remettre à iso8601, qui renvoie des horodatages avec des informations de fuseau horaire correctes. Cela rend les horodatages directement comparables.

Joe D'Andrea
la source
1
Il s'agit d'une recommandation légèrement trompeuse. La règle d'or est de ne jamais traiter les fuseaux horaires. Stockez et transmettez toujours les objets utc non connus (objets d'époque). Le fuseau horaire ne doit être calculé qu'au moment de la représentation dans l'interface utilisateur
nehem le
1
On dirait que cela correspond déjà assez bien aux pensées de Julien. Lesquelles de ses recommandations spécifiques (mentionnées ci-dessus) sont trompeuses?
Joe D'Andrea
10

Pour ajouter des timezoneinformations dans Python 3.2+

import datetime

>>> d = datetime.datetime.now(tz=datetime.timezone.utc)
>>> print(d.tzinfo)
'UTC+00:00'
nordborn
la source
1
AttributeError: 'module' object has no attribute 'timezone' Python 2.7.13 (par défaut, 19 janvier 2017, 14:48:08)
Marcin Owsiany
-6
from datetime import datetime 
from dateutil.relativedelta import relativedelta
d = datetime.now()
date = datetime.isoformat(d).split('.')[0]
d_month = datetime.today() + relativedelta(months=1)
next_month = datetime.isoformat(d_month).split('.')[0]
Mrudula Athuluri
la source
-13

Les dates UTC n'ont pas besoin d'informations de fuseau horaire car elles sont UTC, ce qui signifie par définition qu'elles n'ont pas de décalage.

Ignacio Vazquez-Abrams
la source
10
Pour autant que je sache sur docs.python.org/library/datetime.html , un datetime sans tzinfo est celui où le fuseau horaire n'est pas spécifié. Ici, le fuseau horaire a été spécifié, il devrait donc logiquement être présent. Il y a une grande différence entre une date / heure sans fuseau horaire associé et une qui est définitivement en UTC. (Idéalement, ils devraient être de types différents OMI, mais c'est une autre affaire ...)
Jon Skeet
@ JonSkeet Je pense que vous manquez le point d'Ignacio selon lequel l'UTC n'est pas un fuseau horaire. Incroyable que cette réponse ait un score de -9 lorsque je tape ceci ...
CS
3
@CS: Eh bien, Ignacio n'a jamais déclaré que ... et bien que l'UTC à proprement parler ne soit pas un fuseau horaire, il est généralement traité comme un seul pour rendre la vie considérablement plus simple (y compris en Python, par exemple avec pytz.utc). Notez qu'il y a une grande différence entre une valeur dont le décalage par rapport à l'UTC est inconnue et une où elle est connue pour être 0. Ce dernier est ce qui utcnow() devrait retourner, IMO. Cela cadrerait avec "Un objet conscient est utilisé pour représenter un moment précis qui n'est pas ouvert à l'interprétation" selon la documentation.
Jon Skeet