Convertir un datetime UTC python en un datetime local en utilisant uniquement la bibliothèque standard Python?

J'ai une instance Python datetime qui a été créée en utilisant datetime.utcnow() et persistant dans la base de données.

Pour l'affichage, Je voudrais convertir l'instance datetime extraite de la base de données en datetime locale en utilisant le fuseau horaire local par défaut (c'est-à-dire, comme si le datetime avait été créé en utilisant datetime.maintenant()).

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

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

100
demandé sur FunkySayu 2010-12-30 17:14:41

9 réponses

En Python 3.3+:

from datetime import timezone

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

En 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)

En utilisant pytz (les deux 2/3 Python):

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

Exemple

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: il prend en compte DST et le changement récent de décalage utc pour le fuseau horaire MSK.

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

153
répondu jfs 2013-07-17 15:42:00

Vous ne pouvez pas le faire avec seulement la bibliothèque standard car la bibliothèque standard n'a pas de fuseaux horaires. Vous avez besoin d' pytz ou dateutil.

>>> from datetime import datetime
>>> now = datetime.utcnow()
>>> from dateutil import tz
>>> HERE = tz.tzlocal()
>>> UTC = tz.gettz('UTC')

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

Ou bien, vous pouvez le faire sans pytz ou dateutil en implémentant vos propres fuseaux horaires. Mais ce serait ridicule.

36
répondu Lennart Regebro 2010-12-30 14:56:24

Je pense que je l'ai compris: calcule le nombre de secondes depuis l'époque, puis convertit en un timzeone local en utilisant le temps.localtime, puis convertit la structure time 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é/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)
5
répondu Nitro Zark 2010-12-30 15:02:47

La bibliothèque Python standard ne contient aucune implémentation tzinfo. J'ai toujours considéré cela comme une lacune surprenante du module datetime.

La documentation pour la classe tzinfo vient avec quelques exemples utiles. Chercher le grand bloc de code à la fin de la section.

4
répondu Mark Ransom 2010-12-30 17:29:37

Vous ne pouvez pas le faire avec la bibliothèque standard. En utilisant le module pytz , Vous pouvez convertir n'importe quel objet DateTime naïf/conscient en n'importe quel autre fuseau horaire. Voyons quelques exemples en utilisant Python 3.

Objets naïfs créés par la méthode de classe utcnow()

Pour convertir un naïf objet à un autre fuseau horaire, vous devez d'abord le convertir en conscient objet datetime. Vous pouvez utiliser la méthode replace pour convertir un objetnaïf datetime en un objet datetimeconscient . Ensuite, pour convertir un objet datetimeaware en un autre fuseau horaire, vous pouvez utiliser la méthode astimezone.

La variable pytz.all_timezones vous donne la liste de tous les fuseaux horaires disponibles dans pytz module.

import datetime,pytz

dtobj1=datetime.datetime.utcnow()   #utcnow class method
print(dtobj1)

dtobj3=dtobj1.replace(tzinfo=pytz.UTC) #replace method

dtobj_hongkong=dtobj3.astimezone(pytz.timezone("Asia/Hong_Kong")) #astimezone method
print(dtobj_hongkong)

Objets naïfs créés par la méthode de classe now()

Étant donné que la méthode now renvoie la date et l'heure actuelles, vous devez d'abord informer le fuseau horaire de l'objet datetime. La fonction localize convertit un naïf objet datetime dans un objet datetime sensible au fuseau horaire. Ensuite, vous pouvez utiliser la méthode astimezone pour la convertir en un autre fuseau horaire.

dtobj2=datetime.datetime.now()

mytimezone=pytz.timezone("Europe/Vienna") #my current timezone
dtobj4=mytimezone.localize(dtobj2)        #localize function

dtobj_hongkong=dtobj4.astimezone(pytz.timezone("Asia/Hong_Kong")) #astimezone method
print(dtobj_hongkong)
4
répondu N Randhawa 2016-10-12 17:17:10

S'appuyant sur le commentaire D'Alexei. Cela devrait fonctionner pour L'heure D'été aussi.

import time
import datetime

def utc_to_local(dt):
    if time.localtime().tm_isdst:
        return dt - datetime.timedelta(seconds = time.altzone)
    else:
        return dt - datetime.timedelta(seconds = time.timezone)
2
répondu John Kerns 2018-02-03 22:43:37

Un moyen simple (mais peut-être défectueux) qui fonctionne en Python 2 et 3:

import time
import datetime

def utc_to_local(dt):
    return dt - datetime.timedelta(seconds = time.timezone)

Son avantage est qu'il est trivial d'écrire une fonction inverse

0
répondu Alexei Averchenko 2016-03-09 12:15:48

La façon La plus simple que j'ai trouvé est d'obtenir le décalage dans le temps d'où vous, alors déduire de l'heure.

def format_time(ts,offset):
    if not ts.hour >= offset:
        ts = ts.replace(day=ts.day-1)
        ts = ts.replace(hour=ts.hour-offset)
    else:
        ts = ts.replace(hour=ts.hour-offset)
    return ts

Cela fonctionne pour moi, en python 3.5.2.

0
répondu Zld Productions 2016-12-13 21:50:53

Voici une autre façon de changer le fuseau horaire au format datetime (je sais que j'ai gaspillé mon énergie là-dessus mais je n'ai pas vu cette page donc je ne sais pas comment) sans min. et sec. parce que je n'en ai pas besoin pour mon projet:

def change_time_zone(year, month, day, hour):
      hour = hour + 7 #<-- difference
      if hour >= 24:
        difference = hour - 24
        hour = difference
        day += 1
        long_months = [1, 3, 5, 7, 8, 10, 12]
        short_months = [4, 6, 9, 11]
        if month in short_months:
          if day >= 30:
            day = 1
            month += 1
            if month > 12:
              year += 1
        elif month in long_months:
          if day >= 31:
            day = 1
            month += 1
            if month > 12:
              year += 1
        elif month == 2:
          leap_years = [2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036, 2040, 2044, 2048]
          if year in leap_years:
            if day >= 29:
              day = 1
              month += 1
              if month > 12:
                year += 1
          elif year not in leap_years:
            if day >= 28:
              day = 1
              month += 1
              if month > 12:
                year += 1
      return datetime(int(year), int(month), int(day), int(hour), 00)
0
répondu Guest 2018-09-28 20:34:50