Преобразовать Python UTC datetime в локальное datetime, используя только стандартную библиотеку python?

У меня есть экземпляр Python datetime, который был создан с помощью datetime.utcnow() и сохраняется в базе данных.

для отображения я хотел бы преобразовать экземпляр datetime, полученный из базы данных, в локальный datetime, используя локальный часовой пояс по умолчанию (т. е., как если бы datetime был создан с помощью datetime.теперь.))(

Как я могу преобразовать UTC datetime в локальное datetime, используя только стандартную библиотеку python (например, без зависимости pytz)?

кажется, один решением было бы использовать datetime.astimezone (tz ), но как бы вы получили локальный часовой пояс по умолчанию?

9 ответов


В Python 3.3+:

from datetime import timezone

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

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

используя pytz (оба 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

пример

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

выход

В 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

Примечание: он учитывает DST и недавнее изменение смещения utc для часового пояса MSK.

Я не знаю, работают ли решения, отличные от pytz, на Windows.


вы не можете сделать это с только стандартной библиотеки стандартная библиотека не имеет часовых поясов. Вам нужно pytz или 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())

или же, вы можете сделать это без pytz или dateutil, реализуя свои собственные часовые пояса. Но это было бы глупо.


Я кажется понял: вычисляет количество секунд с начала эпохи, затем переводит на местный timzeone через время.localtime, а затем преобразует структуру времени обратно в 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 )

он правильно применяет летнее / зимнее DST:

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

стандартная библиотека Python не поставляется с любым tzinfo реализации вообще. Я всегда считал это удивительным недостатком модуля datetime.

на документация для класса tzinfo есть несколько полезных примеров. Найдите большой блок кода в конце раздела.


вы не можете сделать это с помощью стандартной библиотеки. Используя pytz модуль вы можете преобразовать любой наивный / осведомленный объект datetime в любой другой часовой пояс. Давайте посмотрим некоторые примеры использования Python 3.

наивный объекты, созданные с помощью метода класса utcnow()

преобразование наивный объект в любой другой часовой пояс, сначала вы должны преобразовать его в в курсе объекта datetime. Вы можете использовать replace метод преобразование наивный объект datetime для в курсе объекта datetime. Затем преобразовать в курсе объект datetime для любого другого часового пояса, который вы можете использовать astimezone метод.

переменная pytz.all_timezones дает вам список всех доступных часовых поясов в модуле 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)

наивный объекты, созданные с помощью метода класса now()

, потому что now метод возвращает текущую дату и время, так что вы сначала нужно сделать часовой пояс объекта datetime. The преобразование наивный объект datetime в объект datetime с учетом часового пояса. Тогда вы можете использовать astimezone метод преобразования его в другой часовой пояс.

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)

основываясь на комментарии Алексея. Это должно работать и для 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)

простой (но, возможно, ошибочный) способ, который работает в Python 2 и 3:

import time
import datetime

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

его преимущество в том, что тривиально писать обратную функцию


самый простой способ, который я нашел, - это получить смещение времени where вы, затем вычесть из часа.

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

это работает для меня в Python 3.5.2.


вот еще один способ изменить часовой пояс в формате datetime (я знаю, что потратил свою энергию на это, но я не видел эту страницу, поэтому я не знаю, как) без min. и сек. потому что мне это не нужно для моего проекта:

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)