Преобразование строки даты и времени UTC в локальную дату и время с помощью Python

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

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

datetime.utcfromtimestamp(timestamp)

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

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

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

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

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

Ответ 1

Если вы не хотите предоставлять свои собственные объекты 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 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 Включены методы автоматического обнаружения для часовых поясов (Ярин)

Ответ 2

Здесь существует устойчивый метод, который не зависит от каких-либо внешних библиотек:

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 входного datetime. См. Комментарии к этому ответу для другого решения.

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

def utc2local (utc):
    epoch = time.mktime(utc.timetuple())
    offset = datetime.fromtimestamp (epoch) - datetime.utcfromtimestamp (epoch)
    return utc + offset

Ответ 3

См. документацию 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

Ответ 4

Если вы хотите получить правильный результат даже в течение времени, которое соответствует неоднозначному локальному времени (например, во время перехода на летнее время) и/или локальное смещение 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)

Ответ 5

При использовании 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>)

Ответ 6

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

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

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

Ответ 7

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

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

Ответ 8

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

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

Ответ 9

Вы можете использовать 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).

Ответ 10

import datetime

def utc_str_to_local_str(utc_str: str, utc_format: str, local_format: str):
    """
    :param utc_str: UTC time string
    :param utc_format: format of UTC time string
    :param local_format: format of local time string
    :return: local time string
    """
    temp1 = datetime.datetime.strptime(utc_str, utc_format)
    temp2 = temp1.replace(tzinfo=datetime.timezone.utc)
    local_time = temp2.astimezone()
    return local_time.strftime(local_format)

utc = '2018-10-17T00:00:00.111Z'
utc_fmt = '%Y-%m-%dT%H:%M:%S.%fZ'
local_fmt = '%Y-%m-%dT%H:%M:%S+08:00'
local_string = utc_str_to_local_str(utc, utc_fmt, local_fmt)
print(local_string)   # 2018-10-17T08:00:00+08:00

например, мой часовой пояс - "+08: 00". введите utc = 2018-10-17T00: 00: 00.111Z, тогда я получу вывод = 2018-10-17T08: 00: 00 +08: 00

Ответ 11

Из приведенного здесь ответа вы можете использовать модуль времени для преобразования из 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)

Ответ 12

Вы можете использовать стрелку

from datetime import datetime
import arrow

now = datetime.utcnow()

print(arrow.get(now).to('local').format())
# '2018-04-04 15:59:24+02:00'

Вы можете кормить arrow.get() чем угодно. отметка времени, строка iso и т.д.