340 votes

Comment convertir une date UTC en date locale en utilisant uniquement la bibliothèque standard ?

J'ai un python datetime qui a été créée à l'aide de datetime.utcnow() et persisté dans la base de données.

Pour l'affichage, je voudrais convertir le datetime récupérée dans la base de données vers la base de données locale datetime en utilisant le fuseau horaire local par défaut (c'est-à-dire comme si l'option datetime a été créé en utilisant datetime.now() ).

Comment puis-je convertir l'UTC datetime à un local datetime en utilisant uniquement la bibliothèque standard de python (par exemple, pas de pytz dépendance) ?

Il semble qu'une solution serait d'utiliser datetime.astimezone(tz) mais comment obtenir le fuseau horaire local par défaut ?

0 votes

Dans quel format l'heure a-t-elle été persistée dans la base de données ? S'il s'agit d'un format standard, il se peut que vous n'ayez pas besoin d'effectuer de conversion.

2 votes

Ce site réponse montre une façon simple d'utiliser pytz .

1 votes

Impossible de trouver une réponse sans recourir à pytz . Sentez-vous bête.

448voto

J.F. Sebastian Points 102961

Dans Python 3.3+ :

from datetime import datetime, timezone

def utc_to_local(utc_dt):
    return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)

Dans Python 2/3 :

import calendar
from datetime import datetime, timedelta

def utc_to_local(utc_dt):
    # get integer timestamp to avoid precision lost
    timestamp = calendar.timegm(utc_dt.timetuple())
    local_dt = datetime.fromtimestamp(timestamp)
    assert utc_dt.resolution >= timedelta(microseconds=1)
    return local_dt.replace(microsecond=utc_dt.microsecond)

Utilisation de pytz (tous deux en Python 2/3) :

import pytz

local_tz = pytz.timezone('Europe/Moscow') # use your local timezone name here
# NOTE: pytz.reference.LocalTimezone() would produce wrong result here

## You could use `tzlocal` module to get local timezone on Unix and Win32
# from tzlocal import get_localzone # $ pip install tzlocal

# # get local timezone    
# local_tz = get_localzone()

def utc_to_local(utc_dt):
    local_dt = utc_dt.replace(tzinfo=pytz.utc).astimezone(local_tz)
    return local_tz.normalize(local_dt) # .normalize might be unnecessary

Ejemplo

def aslocaltimestr(utc_dt):
    return utc_to_local(utc_dt).strftime('%Y-%m-%d %H:%M:%S.%f %Z%z')

print(aslocaltimestr(datetime(2010,  6, 6, 17, 29, 7, 730000)))
print(aslocaltimestr(datetime(2010, 12, 6, 17, 29, 7, 730000)))
print(aslocaltimestr(datetime.utcnow()))

Sortie

Python 3.3

2010-06-06 21:29:07.730000 MSD+0400
2010-12-06 20:29:07.730000 MSK+0300
2012-11-08 14:19:50.093745 MSK+0400

Python 2

2010-06-06 21:29:07.730000 
2010-12-06 20:29:07.730000 
2012-11-08 14:19:50.093911 

pytz

2010-06-06 21:29:07.730000 MSD+0400
2010-12-06 20:29:07.730000 MSK+0300
2012-11-08 14:19:50.146917 MSK+0400

Note : elle tient compte de l'heure d'été et du récent changement de l'offset utc pour le fuseau horaire MSK.

Je ne sais pas si les solutions non-pytz fonctionnent sous Windows.

3 votes

Que fait "normaliser" ?

4 votes

@avi : en général : pytz : Pourquoi normalize est nécessaire lors de la conversion entre les fuseaux horaires ? . Note : Il n'est pas nécessaire avec l'implémentation actuelle de l'option .normalize() car le fuseau horaire source de .astimezone() est UTC.

2 votes

Je reçois TypeError: replace() takes no keyword arguments en essayant la solution pytz.

99voto

Lennart Regebro Points 52510

Depuis Python 3.9, vous pouvez utiliser la fonction zoneinfo module.

Tout d'abord, nous allons obtenir ce temps avec utcnow() :

>>> from datetime import datetime
>>> database_time = datetime.utcnow()
>>> database_time
datetime.datetime(2021, 9, 24, 4, 18, 27, 706532)

Créez ensuite les fuseaux horaires :

>>> from zoneinfo import ZoneInfo
>>> utc = ZoneInfo('UTC')
>>> localtz = ZoneInfo('localtime')

Alors convertissez-vous. Pour convertir entre les fuseaux horaires, la datetime doit savoir dans quel fuseau horaire elle se trouve, alors nous utilisons simplement astimezone() :

>>> utctime = database_time.replace(tzinfo=utc)
>>> localtime = utctime.astimezone(localtz)
>>> localtime
datetime.datetime(2021, 9, 24, 6, 18, 27, 706532, tzinfo=zoneinfo.ZoneInfo(key='localtime'))

Pour Python 3.6 à 3.8, vous avez besoin du module backports.zoneinfo :

>>> try:
>>>     from zoneinfo import ZoneInfo
>>> except ImportError:
>>>     from backports.zoneinfo import ZoneInfo

Le reste est identique.

Pour les versions antérieures à ce besoin pytz o dateutil . datutil fonctionne de manière similaire à zoneinfo :

>>> from dateutil import tz
>>> utc = tz.gettz('UTC')
>>> localtz = tz.tzlocal()

The Conversion:
>>> utctime = now.replace(tzinfo=UTC)
>>> localtime = utctime.astimezone(localtz)
>>> localtime
datetime.datetime(2010, 12, 30, 15, 51, 22, 114668, tzinfo=tzlocal())

pytz a une interface différente qui est le résultat de la gestion des fuseaux horaires de Python qui ne gère pas les heures ambigües :

>>> import pytz
>>> utc = pytz.timezone('UTC')
# There is no local timezone support, you need to know your timezone
>>> localtz = pytz.timezone('Europe/Paris')

>>> utctime = utc.localize(database_time)
>>> localtime = localtz.normalize(utctime.astimezone(localtz))
>>> localtime

0 votes

Merci, je vais garder ça sous la main si jamais j'ai besoin d'une solution complète. Est-ce que dateutil supporte Python 3.1 (il dit Python 2.3+ mais c'est suspect) ?

0 votes

0 votes

Pytz gère en effet mieux les changements de DST.

6voto

Nitro Zark Points 193

Je pense avoir trouvé la solution : calculer le nombre de secondes depuis l'époque, puis convertir en un timzeone local en utilisant time.localtime, et enfin reconvertir la structure de temps en datetime...

EPOCH_DATETIME = datetime.datetime(1970,1,1)
SECONDS_PER_DAY = 24*60*60

def utc_to_local_datetime( utc_datetime ):
    delta = utc_datetime - EPOCH_DATETIME
    utc_epoch = SECONDS_PER_DAY * delta.days + delta.seconds
    time_struct = time.localtime( utc_epoch )
    dt_args = time_struct[:6] + (delta.microseconds,)
    return datetime.datetime( *dt_args )

Il applique correctement l'heure d'été et d'hiver :

>>> utc_to_local_datetime( datetime.datetime(2010, 6, 6, 17, 29, 7, 730000) )
datetime.datetime(2010, 6, 6, 19, 29, 7, 730000)
>>> utc_to_local_datetime( datetime.datetime(2010, 12, 6, 17, 29, 7, 730000) )
datetime.datetime(2010, 12, 6, 18, 29, 7, 730000)

1 votes

Ugh. Mais cela devrait fonctionner sur Unix, au moins. Pour Windows, cela peut être incorrect si vos règles d'heure d'été ont changé depuis la conversion de la date. Pourquoi ne voulez-vous pas utiliser une bibliothèque ?

0 votes

Je n'ai besoin que de la conversion utc/locale, donc faire traîner une telle bibliothèque semble être superflu. Comme je suis le seul utilisateur de l'application, je peux vivre avec certaines limitations. Cela évite aussi de gérer les problèmes liés aux dépendances (support de Python 3 ?, Windows ? qualité...) qui seraient plus coûteux que de travailler sur mon petit script.

1 votes

Eh bien, si vous avez besoin de Python3, vous n'avez pas de chance. Mais sinon, rechercher et créer votre propre solution est plus compliqué que d'utiliser une bibliothèque.

4voto

Mark Ransom Points 132545

La bibliothèque standard de Python n'est pas livrée avec des fichiers tzinfo du tout. J'ai toujours considéré qu'il s'agissait d'une lacune surprenante du module datetime.

El documentation pour la classe tzinfo est accompagné de quelques exemples utiles. Recherchez le grand bloc de code à la fin de la section.

1 votes

Je pense que l'implémentation de base est due au fait que la base de données des fuseaux horaires doit être mise à jour régulièrement car certains pays n'ont pas de règles fixes pour déterminer les périodes de l'heure d'été. Si je ne me trompe pas, la JVM, par exemple, doit être mise à jour pour obtenir la dernière base de données de fuseaux horaires...

0 votes

@Nitro Zark, au moins ils auraient dû en avoir un pour l'UTC. Le os aurait pu en fournir un pour l'heure locale, basé sur les fonctions du système d'exploitation.

2 votes

Je vérifiais la liste des nouvelles fonctionnalités de la 3.2 et ils ont ajouté le fuseau horaire UTC dans la 3.2 ( docs.python.org/dev/whatsnew/3.2.html#datetime ). Il ne semble pas y avoir de fuseau horaire local cependant...

1voto

juan Points 153

Ce site réponse montre une façon simple d'utiliser pytz .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X