Преобразовать строку даты и времени UTC в местную дату и время - PullRequest
181 голосов
/ 22 января 2011

Мне никогда не приходилось переводить время в и из UTC.Недавно у меня был запрос, чтобы мое приложение знало часовой пояс, и я бегал сам по кругу.Много информации о преобразовании местного времени в UTC, которое я нашел довольно элементарным (возможно, я тоже делаю это неправильно), но я не могу найти никакой информации о простом преобразовании времени UTC в часовой пояс конечных пользователей.

В двух словах, android app отправляет мне (appengine app) данные, и в этих данных есть временная метка.Чтобы сохранить эту временную метку в UTC время, я использую:

datetime.utcfromtimestamp(timestamp)

Это, кажется, работает.Когда мое приложение хранит данные, они сохраняются на 5 часов вперед (я EST -5)

Данные хранятся в BigTable appengine, и при получении они выглядят как строка:

"2011-01-21 02:37:21"

Как преобразовать эту строку в DateTime в часовом поясе пользователя?

Кроме того, каково рекомендуемое хранилище для информации о часовом поясе пользователя?(Как вы обычно храните информацию tz, например: «-5: 00» или «EST» и т. Д. И т. Д.?) Я уверен, что ответ на мой первый вопрос может содержать параметр, а ответы - второй.

Ответы [ 12 ]

345 голосов
/ 23 января 2011

Если вы не хотите предоставлять свои собственные tzinfo объекты, проверьте библиотеку python-dateutil . Он обеспечивает tzinfo реализации поверх zoneinfo (Olson) базы данных , так что вы можете ссылаться на правила часовых поясов по несколько каноническому имени.

from datetime import datetime
from dateutil import tz

# METHOD 1: Hardcode zones:
from_zone = tz.gettz('UTC')
to_zone = tz.gettz('America/New_York')

# METHOD 2: Auto-detect zones:
from_zone = tz.tzutc()
to_zone = tz.tzlocal()

# utc = datetime.utcnow()
utc = datetime.strptime('2011-01-21 02:37:21', '%Y-%m-%d %H:%M:%S')

# Tell the datetime object that it's in UTC time zone since 
# datetime objects are 'naive' by default
utc = utc.replace(tzinfo=from_zone)

# Convert time zone
central = utc.astimezone(to_zone)

Редактировать Расширенный пример, чтобы показать strptime использование

Редактировать 2 Исправлено использование API, чтобы показать лучший метод точки входа

Редактировать 3 Включены методы автоопределения часовых поясов (Ярин)

36 голосов
/ 08 октября 2013

Вот гибкий метод, который не зависит от каких-либо внешних библиотек:

from datetime import datetime
import time

def datetime_from_utc_to_local(utc_datetime):
    now_timestamp = time.time()
    offset = datetime.fromtimestamp(now_timestamp) - datetime.utcfromtimestamp(now_timestamp)
    return utc_datetime + offset

Это позволяет избежать проблем с синхронизацией в примере с DelboyJay. И меньшие временные проблемы в поправке Эрика ван Остена.

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

offset = datetime.fromtimestamp(0) - datetime.utcfromtimestamp(0) # NO!

Обновление: У этого фрагмента есть недостаток использования смещения UTC текущего времени, которое может отличаться от смещения UTC входной даты и времени. См. Комментарии к этому ответу для другого решения.

Чтобы обойти разные времена, возьмите время эпохи с прошедшего времени. Вот что я делаю:

def utc2local (utc):
    epoch = time.mktime(utc.timetuple())
    offset = datetime.fromtimestamp (epoch) - datetime.utcfromtimestamp (epoch)
    return utc + offset
21 голосов
/ 23 января 2011

См. Документацию datetime для tzinfo объектов.Вы должны внедрить часовые пояса, которые хотите поддерживать сами.Ниже приведены примеры в нижней части документации.

Вот простой пример:

from datetime import datetime,tzinfo,timedelta

class Zone(tzinfo):
    def __init__(self,offset,isdst,name):
        self.offset = offset
        self.isdst = isdst
        self.name = name
    def utcoffset(self, dt):
        return timedelta(hours=self.offset) + self.dst(dt)
    def dst(self, dt):
            return timedelta(hours=1) if self.isdst else timedelta(0)
    def tzname(self,dt):
         return self.name

GMT = Zone(0,False,'GMT')
EST = Zone(-5,False,'EST')

print datetime.utcnow().strftime('%m/%d/%Y %H:%M:%S %Z')
print datetime.now(GMT).strftime('%m/%d/%Y %H:%M:%S %Z')
print datetime.now(EST).strftime('%m/%d/%Y %H:%M:%S %Z')

t = datetime.strptime('2011-01-21 02:37:21','%Y-%m-%d %H:%M:%S')
t = t.replace(tzinfo=GMT)
print t
print t.astimezone(EST)

Вывод

01/22/2011 21:52:09 
01/22/2011 21:52:09 GMT
01/22/2011 16:52:09 EST
2011-01-21 02:37:21+00:00
2011-01-20 21:37:21-05:00a
15 голосов
/ 02 октября 2015

Если вы хотите получить правильный результат даже для времени, которое соответствует неоднозначному местному времени (например, во время перехода на летнее время) и / или местное смещение utc отличается в разное время в вашем местном часовом поясе, тогда используйте pytz часовые пояса:

#!/usr/bin/env python
from datetime import datetime
import pytz    # $ pip install pytz
import tzlocal # $ pip install tzlocal

local_timezone = tzlocal.get_localzone() # get pytz tzinfo
utc_time = datetime.strptime("2011-01-21 02:37:21", "%Y-%m-%d %H:%M:%S")
local_time = utc_time.replace(tzinfo=pytz.utc).astimezone(local_timezone)
7 голосов
/ 21 сентября 2017

Этот ответ может быть полезен, если вы не хотите использовать какие-либо другие модули, кроме datetime.

datetime.utcfromtimestamp(timestamp) возвращает наивный datetime объект (не осведомленный).Осведомленные осведомлены о часовом поясе, а наивные - нет.Вам нужен осведомленный, если вы хотите конвертировать между часовыми поясами (например, между UTC и местным временем).

Если вы не тот, кто создает дату для начала, но вы все равно можете создать наивный datetime объект во времени UTC, вы можете попробовать этот код Python 3.x для его преобразования:

import datetime

d=datetime.datetime.strptime("2011-01-21 02:37:21", "%Y-%m-%d %H:%M:%S") #Get your naive datetime object
d=d.replace(tzinfo=datetime.timezone.utc) #Convert it to an aware datetime object in UTC time.
d=d.astimezone() #Convert it to your local timezone (still aware)
print(d.strftime("%d %b %Y (%I:%M:%S:%f %p) %Z")) #Print it with a directive of choice

Будьте осторожны, чтобы не ошибочно предположить, что если ваш часовой пояс в настоящее время MDT, переход на летнее время не работаетс вышеуказанным кодом, так как он печатает MST.Вы заметите, что если вы измените месяц на август, он напечатает MDT.

Другой простой способ получить осведомленный объект datetime (также в Python 3.x) - создать его с помощьючасовой пояс, указанный для начала.Вот пример использования UTC:

import datetime, sys

aware_utc_dt_obj=datetime.datetime.now(datetime.timezone.utc) #create an aware datetime object
dt_obj_local=aware_utc_dt_obj.astimezone() #convert it to local time

#The following section is just code for a directive I made that I liked.
if sys.platform=="win32":
    directive="%#d %b %Y (%#I:%M:%S:%f %p) %Z"
else:
    directive="%-d %b %Y (%-I:%M:%S:%f %p) %Z"

print(dt_obj_local.strftime(directive))

Если вы используете Python 2.x, вам, вероятно, придется создать подкласс datetime.tzinfo и использовать его, чтобы помочь вам создать осведомленный объект datetime, поскольку datetime.timezone не существует в Python 2.x.

6 голосов
/ 04 августа 2014

При использовании django вы можете использовать метод timezone.localtime (см. https://docs.djangoproject.com/en/dev/topics/i18n/timezones/).

from django.utils import timezone
date 
# datetime.datetime(2014, 8, 1, 20, 15, 0, 513000, tzinfo=<UTC>)

timezone.localtime(date)
# datetime.datetime(2014, 8, 1, 16, 15, 0, 513000, tzinfo=<DstTzInfo 'America/New_York' EDT-1 day, 20:00:00 DST>)
1 голос
/ 13 декабря 2018

Из ответа здесь вы можете использовать модуль времени для преобразования из utc в местное время, установленное на вашем компьютере:

utc_time = time.strptime("2018-12-13T10:32:00.000", "%Y-%m-%dT%H:%M:%S.%f")
utc_seconds = calendar.timegm(utc_time)
local_time = time.localtime(utc_seconds)
1 голос
/ 08 июня 2017

Вы можете использовать calendar.timegm для преобразования времени в секунды после эпохи Unix и time.localtime для обратного преобразования:

import calendar
import time

time_tuple = time.strptime("2011-01-21 02:37:21", "%Y-%m-%d %H:%M:%S")
t = calendar.timegm(time_tuple)

print time.ctime(t)

Дает Fri Jan 21 05:37:21 2011 (потому что я в UTC + 03: 00часовой пояс).

1 голос
/ 15 апреля 2013

Вот быстрая и грязная версия, которая использует настройки локальных систем для определения разницы во времени.ПРИМЕЧАНИЕ. Это не будет работать, если вам нужно преобразовать в часовой пояс, в котором ваша текущая система не работает. Я проверил это с настройками в Великобритании в BST timezone

from datetime import datetime
def ConvertP4DateTimeToLocal(timestampValue):
   assert isinstance(timestampValue, int)

   # get the UTC time from the timestamp integer value.
   d = datetime.utcfromtimestamp( timestampValue )

   # calculate time difference from utcnow and the local system time reported by OS
   offset = datetime.now() - datetime.utcnow()

   # Add offset to UTC time and return it
   return d + offset
1 голос
/ 23 января 2011

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

Для веб-приложения это довольно легко сделать в javascript - вы можете довольно легко вычислить смещение часового пояса браузера, используя встроенные методы, а затем правильно отобразить данные из бэкэнда.

...