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

  1. Сначала убедитесь, что на вашем компьютере/сервере указан правильный часовой пояс. Иногда вы можете видеть правильное время, но используете неправильный часовой пояс, потому что система корректирует время из Интернета.

Это мой рабочий код, позволяющий автоматически определять часовой пояс и поддерживать часовой пояс летнего времени:

      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
Другие вопросы по тегам