Python: как преобразовать дату/время из одного часового пояса в другой часовой пояс?

В частности, учитывая часовой пояс моего сервера (перспектива системного времени) и ввод часового пояса, как мне рассчитать системное время, как если бы оно было в этом новом часовом поясе (независимо от летнего времени и т. д.)?

import datetime
current_time = datetime.datetime.now() #system time

server_timezone = "US/Eastern"
new_timezone = "US/Pacific"

current_time_in_new_timezone = ???

person kfan    schedule 13.08.2015    source источник


Ответы (3)


Если вы знаете свой исходный часовой пояс и новый часовой пояс, в который хотите его преобразовать, все оказывается очень просто:

  1. Создайте два объекта pytz.timezone, один для текущего часового пояса и один для нового часового пояса, например. pytz.timezone("US/Pacific"). Вы можете найти список всех официальных часовых поясов в pytz библиотеке: import pytz; pytz.all_timezones

  2. Локализуйте интересующую дату и время/временную метку для текущего часового пояса, например.

current_timezone = pytz.timezone("US/Eastern")
localized_timestamp = current_timezone.localize(timestamp)
  1. Преобразуйте в новый часовой пояс, используя .astimezone() для новой локализованной даты и времени/временной метки из шага 2 с требуемым объектом pytz часового пояса в качестве входных данных, например. localized_timestamp.astimezone(new_timezone).

Сделанный!

В качестве полного примера:

import datetime
import pytz

# a timestamp I'd like to convert
my_timestamp = datetime.datetime.now()

# create both timezone objects
old_timezone = pytz.timezone("US/Eastern")
new_timezone = pytz.timezone("US/Pacific")

# two-step process
localized_timestamp = old_timezone.localize(my_timestamp)
new_timezone_timestamp = localized_timestamp.astimezone(new_timezone)

# or alternatively, as an one-liner
new_timezone_timestamp = old_timezone.localize(my_timestamp).astimezone(new_timezone) 

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

datetime.datetime.now(new_timezone)

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

Помимо летнего времени, время в программном обеспечении может быть довольно сложным. Чтобы понять, насколько сложно иметь дело со временем в программном обеспечении вообще, вот потенциально полезный ресурс: http://yourcalendricalfallacyis.com

Даже такая, казалось бы, простая операция, как преобразование даты и времени/временной метки в дату, может стать неочевидной. Как указано в этой полезной документации:

Datetime представляет момент времени. Он абсолютен: он ни от чего не зависит. Напротив, дата — это концепция календаря. Это период времени, границы которого зависят от часового пояса, в котором считается дата. Как видите, эти два понятия принципиально разные.

Понимание этой разницы — ключевой шаг к тому, чтобы избежать ошибок, связанных со временем. Удачи.

person kfan    schedule 13.08.2015
comment
(1) если вам нужно текущее время, используйте datetime.now(server_timezone) вместо server_timezone.localize(datetime.now()) (2) рассмотрите возможность использования параметра is_dst (3) Вам может понадобиться server_timezone.normalize() после server_timezone.localize(), чтобы исправить несуществующее местное время. См. более подробную информацию и ссылки в моем ответе - person jfs; 23.10.2015
comment
Я считаю, что в вашем примере «timezone_localized_timestamp» должно быть «localized_timestamp». Или наоборот. В остальном работает отлично. - person Rivers Cuomo; 25.08.2020

Как вы конвертируете дату и время/отметку времени из одного часового пояса в другой часовой пояс?

Есть два шага:

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

    #!/usr/bin/env python
    from datetime import datetime
    import pytz
    
    server_timezone = pytz.timezone("US/Eastern")
    server_time = datetime.now(server_timezone) # you could pass *tz* directly
    

    Примечание. datetime.now(server_timezone) работает даже в неоднозначное время, например, во время перехода на летнее время, в то время как server_timezone.localize(datetime.now()) может дать сбой (вероятность 50%).

    Если вы уверены, что ваше время ввода существует в часовом поясе сервера и оно уникально, вы можете передать is_dst=None, чтобы подтвердить, что:

    server_time = server_timezone.localize(naive_time, is_dst=None)
    

    Это вызывает исключение для недопустимого времени.
    Если допустимо игнорировать ошибку до дня (хотя обычно ошибка из-за перехода на летнее время составляет около часа), тогда вы может удалить параметр is_dst:

    server_time = server_timezone.normalize(server_timezone.localize(naive_time))
    

    .normalize() вызывается для корректировки несуществующего времени (местное время в промежутке, во время переходов "пружинного вперед"). Если правила часового пояса не изменились; ваш сервер не должен генерировать несуществующее время. См. "Можно ли всегда устанавливать is_dst=True?"

  2. Преобразуйте осведомленный объект даты и времени в целевой часовой пояс tz:

    tz = pytz.timezone("US/Pacific")
    server_time_in_new_timezone = server_time.astimezone(tz)
    
person jfs    schedule 23.10.2015

В Python 3.9 в стандартной библиотеке есть все, что вам нужно: zoneinfo. pytz больше не нужен (устарел; -› устаревающая прокладка pytz).

Ex:

from datetime import datetime
from zoneinfo import ZoneInfo

server_timezone = "US/Eastern"
new_timezone = "US/Pacific"

current_time = datetime.now(ZoneInfo(server_timezone)) 

# current_time_in_new_timezone = ???
current_time_in_new_timezone = current_time.astimezone(ZoneInfo(new_timezone))

print(current_time_in_new_timezone)
print(repr(current_time_in_new_timezone))

# 2021-05-02 02:02:38.976798-07:00
# datetime.datetime(2021, 5, 2, 2, 2, 38, 976798, tzinfo=zoneinfo.ZoneInfo(key='US/Pacific'))
person MrFuppes    schedule 02.05.2021