2) [H] Как правильно локализовать наивное время? datetime.datetime
использует my_datetime.localize(pytz_spanish_timezone)
На самом деле, все наоборот. localize
- это метод Pyzz для часовых поясов, а не datetime
метод:
import pytz
madrid = pytz.timezone('Europe/Madrid')
aware_datetime = madrid.localize(naive_datetime)
Тебе нужен datetime.datetime
здесь. Для datetime.time
объектов нет эквивалента. См. Ниже причину, по которой.
3) Как преобразовать один datetime.time
объект из часового пояса в другой?
Рассмотрим следующую ситуацию: Мы знаем, что время 20:30, а часовой пояс Europe/Madrid
, и мы хотим преобразовать его в UTC.
Результат будет отличаться в зависимости от того, попадает ли дата в летнее время (CEST) или нет (CET):
Например,
import datetime as DT
import pytz
madrid = pytz.timezone('Europe/Madrid')
utc = pytz.utc
CET_date = madrid.localize(DT.datetime(2019, 3, 30, 20, 30, 0), is_dst=None)
# the most recent transition occurred at `2019-03-31 02:00:00+01:00 CEST`
CEST_date = madrid.localize(DT.datetime(2019, 3, 31, 20, 30, 0), is_dst=None)
print(CET_date.astimezone(utc))
print(CEST_date.astimezone(utc))
# 2019-03-30 19:30:00+00:00
# 2019-03-31 18:30:00+00:00
Обратите внимание, что когда дата находится в CET, время 20:30 «конвертируется» в 19:30, но когда дата в CEST, время конвертируется в 18:30.
Нет (простого) ответа на ваш вопрос без предварительного знания даты.
4a) Как хранить datetime.time
в базе данных postgresql? Я знаю, что существуют типы данных time и timetz.
За документы :
Тип time with time zone
определяется стандартом SQL, но определение обладает свойствами, которые приводят к сомнительной полезности.
Я думаю, что документы ссылаются на проблему, показанную выше. Не используйте time with
time zone
. Если вы хотите сохранить время, используйте тип PostgreSQL plain time
.
Вы можете сохранить time
и timezone
в базе данных и восстановить
дата-время с учетом часового пояса, когда у вас есть дата. Но обратите внимание, что есть
подводные камни:
Существуют неоднозначные локальные даты
import datetime as DT
import pytz
madrid = pytz.timezone('Europe/Madrid')
date = madrid.localize(DT.datetime(2019, 10, 27, 2, 0, 0), is_dst=None)
повышает pytz.exceptions.AmbiguousTimeError: 2019-10-27 02:00:00
.
Чтобы избежать AmbiguousTimeError
, is_dst
должен быть указан явно:
import datetime as DT
import pytz
madrid = pytz.timezone('Europe/Madrid')
date = madrid.localize(DT.datetime(2019, 10, 27, 2, 0, 0), is_dst=False)
print(date)
date = madrid.localize(DT.datetime(2019, 10, 27, 2, 0, 0), is_dst=True)
print(date)
# 2019-10-27 02:00:00+01:00
# 2019-10-27 02:00:00+02:00
Существует несуществующее местное время
import datetime as DT
import pytz
madrid = pytz.timezone('Europe/Madrid')
madrid.localize(DT.datetime(2019, 3, 31, 2, 0, 0), is_dst=None)
поднимает pytz.exceptions.NonExistentTimeError: 2019-03-31 02:00:00
Вы можете избежать ошибки NonExistentTimeError, указав, относится ли наивное локальное время ко времени в летнее время (летнее время):
import datetime as DT
import pytz
madrid = pytz.timezone('Europe/Madrid')
date = madrid.normalize(madrid.localize(DT.datetime(2019, 3, 31, 2, 0, 0), is_dst=False))
print(date)
date = madrid.normalize(madrid.localize(DT.datetime(2019, 3, 31, 2, 0, 0), is_dst=True))
print(date)
# 2019-03-31 03:00:00+02:00
# 2019-03-31 01:00:00+01:00
Могут существовать даты и время, которые невозможно представить, учитывая местное время и конкретный часовой пояс.
AmbiguousTimeError
и NonExistentTimeError
выше показывают важность определения значения is_dst
.
Чтобы избежать этих ошибок, вам нужно хранить логическое значение is_dst
вместе с time
и timezone
в базе данных.
Вы можете подумать, что можете избежать проблемы, просто выбрав одно значение
is_dst
за все время. Но ты ошибаешься. Вот своеобразный пример
(взято из PyTZ DOCS ), который показывает, если вы
всегда выбирайте is_dst = False
(или is_dst = True
), могут быть даты UTC
который не может быть выражен, учитывая только наивное местное время и часовой пояс!
import datetime as DT
import pytz
warsaw = pytz.timezone('Europe/Warsaw')
utc = pytz.utc
date1 = warsaw.localize(DT.datetime(1915, 8, 4, 23, 35, 59), is_dst=False).astimezone(utc)
date2 = warsaw.localize(DT.datetime(1915, 8, 4, 23, 36, 0), is_dst=False).astimezone(utc)
print('Datetimes between {} and {} can not be expressed if we assume is_dist=False.'.format(date1, date2))
date3 = warsaw.localize(DT.datetime(1915, 8, 4, 23, 59, 59), is_dst=True).astimezone(utc)
date4 = warsaw.localize(DT.datetime(1915, 8, 5, 0, 0, 0), is_dst=True).astimezone(utc)
print('Datetimes between {} and {} can not be expressed if we assume is_dist=True.'.format(date1, date2))
печать
Datetimes between 1915-08-04 22:11:59+00:00 and 1915-08-04 22:36:00+00:00 can not be expressed if we assume is_dist=False.
Datetimes between 1915-08-04 22:11:59+00:00 and 1915-08-04 22:36:00+00:00 can not be expressed if we assume is_dist=True.
4b) Полагаю, мне следует хранить время как UTC. Будет ли иметь значение часовой пояс? Должен ли я хранить его как-нибудь?
По указанным выше причинам в UTC отсутствует время (без даты).
Но вы можете избежать проблем, упомянутых выше, просто сохранив datetimes в UTC.
Если вы создаете таблицу со столбцом с типом данных timestamptz
, то
вы можете использовать адаптер базы данных, такой как psycopg2
, для хранения времени Python с учетом часового пояса
как PostgreSQL timestamptz
с. Когда вы запрашиваете базу данных, psycopg2
преобразует timestamptz
s обратно в
с учетом часовых поясов.
Внутри PostgreSQL хранит все timestamptz
в UTC, но сообщает о значениях относительно
настройка часового пояса пользователя PostgreSQL. На стороне Python, учитывая дату / время с учетом часового пояса,
Вы можете использовать метод astimezone
, чтобы преобразовать его в любой часовой пояс.
Вам не нужно хранить часовой пояс отдельно, если вы не хотите сообщать
разные даты и время относительно разных часовых поясов.
5) Как разобрать время из строки, не переходя через datetime?
Вы можете использовать regex для разбора строк времени:
import re
import datetime as DT
atime = DT.time(*map(int, re.search(r'(\d{,2}):(\d{,2}):(\d{,2})', 'blueberry jam at 13:32:02').groups()))
print(repr(atime))
# datetime.time(13, 32, 2)
Выше шаблон регулярного выражения \d
соответствует одной цифре. \d{1,2}
соответствует 1 или 2 цифрам.
Кроме того, сторонний dateutil пакет может анализировать
временные строки в различных форматах:
import dateutil.parser as DP
print(DP.parse("13:32:02").time())
# 13:32:02
print(DP.parse("blueberry jam at 13:32:02", fuzzy=True).time())
# 13:32:02
print(DP.parse("30 minutes 12 hours").time())
# 12:30:00
print(DP.parse("2:30pm").time())
# 14:30:00
Здесь есть много чего переварить, и, вероятно, можно сказать еще кое-что.
о каждой из этих проблем. В будущем вы можете разделить свой пост на
несколько вопросов. Это снизит барьер для людей, которые могут пожелать
ответьте на один вопрос, но не на все, и вы сможете быстрее получить больше ответов.