web-dev-qa-db-fra.com

Convertir une date/heure UTC python en une date/heure locale en utilisant uniquement la bibliothèque standard Python?

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

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

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

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

116
Nitro Zark

Je pense l'avoir compris: calcule le nombre de secondes depuis Epoch, puis convertit le timzeone local en utilisant time.localtime, puis reconvertit la structure de l'heure en un 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é/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)
6
Nitro Zark

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

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

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
2010-06-06 21:29:07.730000 
2010-12-06 20:29:07.730000 
2012-11-08 14:19:50.093911 
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

Remarque: il tient compte de l'heure d'été et de la modification récente du décalage utc du fuseau horaire MSK.

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

171
jfs

Vous ne pouvez pas le faire uniquement avec la bibliothèque standard car celle-ci n’a pas de fuseau horaire. Vous avez besoin de 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 mettant en place vos propres fuseaux horaires. Mais ce serait idiot.

37
Lennart Regebro

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

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

Pour convertir un objet naive en un autre fuseau horaire, vous devez d’abord le convertir en objet aware datetime. Vous pouvez utiliser la méthode replace pour convertir un objet naive datetime en un objet aware datetime. Ensuite, pour convertir un objet aware datetime 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 le module pytz.

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 rendre l'objet fuseau horaire conscient de la date. La fonction localize convertit un objet naive datetime en un objet datetime compatible avec un fuseau horaire. Ensuite, vous pouvez utiliser la méthode astimezone pour le 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)
5
N Randhawa

La bibliothèque Python standard n’est livrée avec aucune implémentation de tzinfo. J'ai toujours considéré cela comme une faiblesse surprenante du module datetime.

La documentation de la classe tzinfo est fournie avec des exemples utiles. Recherchez le gros bloc de code à la fin de la section.

4
Mark Ransom

S'appuyant sur le commentaire d'Alexei. Cela devrait fonctionner aussi pour DST.

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)
4
John Kerns

Voici une autre façon de changer le fuseau horaire au format datetime (je sais que j'ai perdu mon énergie à ce sujet mais je n'ai pas vu cette page, donc je ne sais pas comment) sans min. et sec. car 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:
          if not year%4==0:
            if day >= 29:
              day = 1
              month += 1
              if month > 12:
                year += 1
          else:
            if day >= 28:
              day = 1
              month += 1
              if month > 12:
                year += 1
      return datetime(int(year), int(month), int(day), int(hour), 00)
0
Guest

Une manière simple (mais peut-être imparfaite) 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

Le moyen le plus simple que j'ai trouvé est d'obtenir le décalage horaire de l'endroit où vous êtes, puis soustrayez cela 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, dans Python 3.5.2.

0
Zld Productions

C'est une façon terrible de le faire mais cela évite de créer une définition. Il remplit l'obligation de s'en tenir à la bibliothèque de base Python3.

# Adjust from UST to Eastern Standard Time (dynamic)
# df.my_localtime should already be in datetime format, so just in case
df['my_localtime'] = pd.to_datetime.df['my_localtime']

df['my_localtime'] = df['my_localtime'].dt.tz_localize('UTC').dt.tz_convert('America/New_York').astype(str)
df['my_localtime'] = pd.to_datetime(df.my_localtime.str[:-6])
0
Arthur D. Howland