Преобразовать дату и время Python UTC в локальную дату, используя только стандартную библиотеку Python? - PullRequest
129 голосов
/ 30 декабря 2010

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

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

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

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

Ответы [ 10 ]

192 голосов
/ 08 ноября 2012

В 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

Пример

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.

38 голосов
/ 30 декабря 2010

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

7 голосов
/ 30 декабря 2010

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

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

>>> 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 голосов
/ 12 октября 2016

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

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

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

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

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 голосов
/ 30 декабря 2010

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

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

0 голосов
/ 26 марта 2019

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

Вот еще один способ изменить часовой пояс в формате 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)
0 голосов
/ 14 декабря 2016

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

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.

0 голосов
/ 09 марта 2016

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

import time
import datetime

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...