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

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

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

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

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

Ответ 1

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

Ответ 2

В Python 3. 3+:

from datetime import datetime, 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
Пример

Example

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

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

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

Ответ 3

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

Ответ 4

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

Наивные объекты, созданные методом класса utcnow()

Чтобы преобразовать объект наивный в любой другой часовой пояс, сначала нужно преобразовать его в объект осведомленный datetime. Вы можете использовать метод replace для преобразования объекта 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. Функция localize преобразует объект 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)

Ответ 5

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

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

Ответ 6

Опираясь на комментарии Алексея. Это должно работать и для летнего времени.

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)

Ответ 7

Этот ответ показывает простой способ использования pytz.

Ответ 8

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

import time
import datetime

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

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

Ответ 9

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

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.

Ответ 10

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

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)

Ответ 11

Это ужасный способ сделать это, но он избегает создания определения. Он соответствует требованию придерживаться базовой библиотеки 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])

Ответ 12

Преобразование и возврат работает с меткой времени в Python3, мои объекты datetime поддерживают временную зону только для импорта datetime, могут быть свободно переписаны для Python2

Результат теста

test result.

import datetime
def mz(dt,tzsecs):
   return dt.replace(tzinfo=datetime.timezone(datetime.timedelta(seconds=tzsecs)))
def local2utc(local):
    return mz(datetime.datetime.utcfromtimestamp(local.timestamp()),0)
def utc2local(utc):
    utc_ts=utc.timestamp()
    local_ts=mz(datetime.datetime.fromtimestamp(utc_ts),0).timestamp()
    return mz(datetime.datetime.fromtimestamp(utc_ts),local_ts-utc_ts)

Я сделал пример, наглядно демонстрирующий это и как использовать мои функции!

fmt="%c %Z"
u=mz(datetime.datetime.utcnow(),0)
dm=u.month-1
dy=u.year
for m in range(12):
    u=u.replace(year=dy+int((dm+m)/12),month=1+(dm+m)%12)
    dt=u
    l=utc2local(u)
    print("Convert",u.strftime(fmt),"  ==  ",l.strftime(fmt))
    u=local2utc(l)
    print("Reverse",l.strftime(fmt),"  ==  ",u.strftime(fmt),dt==u)
print("Noted DST is active in summer (Rome, Stockholm  UTC+2)!")