Преобразование строки даты и времени UTC в локальную дату и время с помощью Python
Мне никогда не приходилось переводить время в и из UTC. Недавно у меня был запрос, чтобы мое приложение знало часовой пояс, и я бегал сам по кругу. Много информации о преобразовании местного времени в UTC, которое я нашел довольно элементарным (возможно, я тоже делаю это неправильно), но я не могу найти никакой информации о том, как легко преобразовать время UTC в часовой пояс конечных пользователей.
В двух словах, android app отправляет мне (appengine app) данные, и в этих данных есть временная метка. Чтобы сохранить эту метку времени в UTC время, которое я использую:
datetime.utcfromtimestamp(timestamp)
Кажется, это работает. Когда мое приложение хранит данные, оно сохраняется как 5 часов вперед (я EST -5)
Данные хранятся в BigTable приложения, и при получении они выглядят как строка:
"2011-01-21 02:37:21"
Как мне преобразовать эту строку в DateTime в часовом поясе пользователя?
Кроме того, каково рекомендуемое хранилище для информации о часовом поясе пользователей? (Как вы обычно храните информацию tz, например: "-5:00" или "EST" и т. Д.?) Я уверен, что ответ на мой первый вопрос может содержать параметр, а ответы - второй.
17 ответов
Если вы не хотите предоставлять свой собственный tzinfo
объекты, проверьте библиотеку http://niemeyer.net/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 Включены методы автоопределения часовых поясов (Ярин)
Вот гибкий метод, который не зависит от каких-либо внешних библиотек:
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
Смотрите документацию 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
Если вы хотите получить правильный результат даже для времени, которое соответствует неоднозначному местному времени (например, во время перехода на летнее время) и / или местное смещение 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)
Этот ответ должен быть полезен, если вы не хотите использовать какие-либо другие модули, кроме 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.
При использовании 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>)
В облачной среде для запада США у меня работало следующее:
import datetime
import pytz
#set the timezone
tzInfo = pytz.timezone('America/Los_Angeles')
dt = datetime.datetime.now(tz=tzInfo)
print(dt)
Консолидация ответа от франкса в удобный метод.
import calendar
import datetime
def to_local_datetime(utc_dt):
"""
convert from utc datetime to a locally aware datetime according to the host timezone
:param utc_dt: utc datetime
:return: local timezone datetime
"""
return datetime.datetime.fromtimestamp(calendar.timegm(utc_dt.timetuple()))
Вы можете использовать стрелку
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 и т. д.
Коротко и просто:
from datetime import datetime
t = "2011-01-21 02:37:21"
datetime.fromisoformat(t) + (datetime.now() - datetime.utcnow())
Это сработало для меня:
from django.utils import timezone
from datetime import timedelta,datetime
ist_time = timezone.now() + timedelta(hours=5,minutes=30)
#second method
ist_time = datetime.now() + timedelta(hours=5,minutes=30)
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'. input utc = 2018-10-17T00: 00: 00.111Z, тогда я получу вывод = 2018-10-17T08: 00: 00 + 08: 00
Ты можешь использовать 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).
Из приведенного здесь ответа вы можете использовать модуль времени для преобразования из 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)
Вот быстрая и грязная версия, которая использует настройки локальных систем для определения разницы во времени. ПРИМЕЧАНИЕ. Это не будет работать, если вам нужно преобразовать в часовой пояс, в котором ваша текущая система не работает. Я проверил это с настройками в Великобритании в часовом поясе 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
Я традиционно откладываю это на внешний интерфейс - отправляю время из серверной части в виде временных меток или какого-либо другого формата даты-времени в UTC, затем позволяю клиенту вычислить смещение часового пояса и отобразить эти данные в правильном часовом поясе.
Для веб-приложения это довольно легко сделать в javascript - вы можете довольно легко вычислить смещение часового пояса браузера, используя встроенные методы, а затем правильно отобразить данные из серверной части.
- Сначала убедитесь, что на вашем компьютере/сервере указан правильный часовой пояс. Иногда вы можете видеть правильное время, но используете неправильный часовой пояс, потому что система корректирует время из Интернета.
Это мой рабочий код, позволяющий автоматически определять часовой пояс и поддерживать часовой пояс летнего времени:
from datetime import datetime, timedelta
def datetime_from_utc_to_local(utc_string ): #"2023-09-30T17:49:06.000Z"
# Extract the offset from the input string, assuming it's always in the format "-HHMM"
local_offset_str = datetime.now().astimezone().isoformat(timespec="seconds")[-6:]
# Convert the offset string to an integer representing hours an minutes
offset_hours = int(local_offset_str[:3])
offset_minutes = int(local_offset_str[4:])
# Create a datetime object from the input string, excluding the "Z" character
utc_datetime = datetime.fromisoformat(utc_string[:-1])
# Apply the offset to convert the GMT datetime to the local time
local_datetime = utc_datetime + timedelta(hours=offset_hours, minutes=offset_minutes)
return local_datetime