Python strptime() и часовые пояса?
У меня есть дамп-файл CSV из резервной копии Blackberry IPD, созданный с использованием IPDDump. Строки даты / времени здесь выглядят примерно так (где EST
является австралийским часовым поясом):
Tue Jun 22 07:46:22 EST 2010
Мне нужно иметь возможность разобрать эту дату в Python. Сначала я попытался использовать strptime()
функция от datettime.
>>> datetime.datetime.strptime('Tue Jun 22 12:10:20 2010 EST', '%a %b %d %H:%M:%S %Y %Z')
Однако по какой-то причине datetime
объект, который возвращается, кажется, не имеет никакого tzinfo
связано с этим.
Я читал на этой странице, что, по-видимому, datetime.strptime
молча отбрасывает tzinfo
Тем не менее, я проверил документацию, и я не могу найти что-либо на этот счет, задокументированное здесь.
Мне удалось проанализировать дату с помощью сторонней библиотеки Python dateutil, однако мне все еще интересно, как я использовал встроенную strptime()
неправильно? Есть ли способ получить strptime()
хорошо играть с часовыми поясами?
6 ответов
datetime
В документации модуля сказано:
Возвращает datetime, соответствующее date_string, проанализированный в соответствии с форматом. Это эквивалентно
datetime(*(time.strptime(date_string, format)[0:6]))
,
Видеть, что [0:6]
? Это получает вас (year, month, day, hour, minute, second)
, Ничего больше. Нет упоминания о часовых поясах.
Интересно, что [Win XP SP2, Python 2.6, 2.7] передает ваш пример time.strptime
не работает, но если вы удалите " %Z" и " EST", это сработает. Также работает "UTC" или "GMT" вместо " EST". "PST" и "MEZ" не работают. Непонятные.
Стоит отметить, что это было обновлено с версии 3.2, и в той же документации теперь также говорится следующее:
Когда директива%z предоставляется методу strptime(), будет создан осведомленный объект datetime. Tzinfo результата будет установлен на экземпляр часового пояса.
Обратите внимание, что это не работает с%Z, поэтому случай важен. Смотрите следующий пример:
In [1]: from datetime import datetime
In [2]: start_time = datetime.strptime('2018-04-18-17-04-30-AEST','%Y-%m-%d-%H-%M-%S-%Z')
In [3]: print("TZ NAME: {tz}".format(tz=start_time.tzname()))
TZ NAME: None
In [4]: start_time = datetime.strptime('2018-04-18-17-04-30-+1000','%Y-%m-%d-%H-%M-%S-%z')
In [5]: print("TZ NAME: {tz}".format(tz=start_time.tzname()))
TZ NAME: UTC+10:00
Я рекомендую использовать http://labix.org/python-dateutil. Его синтаксический анализатор смог проанализировать каждый формат даты, который я выбрасывал.
>>> from dateutil import parser
>>> parser.parse("Tue Jun 22 07:46:22 EST 2010")
datetime.datetime(2010, 6, 22, 7, 46, 22, tzinfo=tzlocal())
>>> parser.parse("Fri, 11 Nov 2011 03:18:09 -0400")
datetime.datetime(2011, 11, 11, 3, 18, 9, tzinfo=tzoffset(None, -14400))
>>> parser.parse("Sun")
datetime.datetime(2011, 12, 18, 0, 0)
>>> parser.parse("10-11-08")
datetime.datetime(2008, 10, 11, 0, 0)
и так далее. Не иметь дело с strptime()
отформатируй ерунду... просто брось на нее дату и она поступит правильно.
Обновление: Ой. Я пропустил в вашем первоначальном вопросе, который вы упомянули, что вы использовали dateutil
, Извини за это. Но я надеюсь, что этот ответ по-прежнему полезен для других людей, которые сталкиваются с этим вопросом, когда у них возникают вопросы разбора даты и они видят полезность этого модуля.
Поскольку
strptime
возвращает объект datetime, который имеет
tzinfo
атрибут, мы можем просто заменить его желаемым часовым поясом.
>>> import datetime
>>> date_time_str = '2018-06-29 08:15:27.243860'
>>> date_time_obj = datetime.datetime.strptime(date_time_str, '%Y-%m-%d %H:%M:%S.%f').replace(tzinfo=datetime.timezone.utc)
>>> date_time_obj.tzname()
'UTC'
Ваша строка времени аналогична формату времени в rfc 2822 (формат даты в электронной почте, заголовки http). Вы можете разобрать его, используя только stdlib:
>>> from email.utils import parsedate_tz
>>> parsedate_tz('Tue Jun 22 07:46:22 EST 2010')
(2010, 6, 22, 7, 46, 22, 0, 1, -1, -18000)
Ознакомьтесь с решениями, которые предоставляют объекты datetime с учетом часового пояса для различных версий Python: анализ даты с часовым поясом из электронного письма.
В этом формате EST
семантически эквивалентно-0500
, Хотя, как правило, сокращения часового пояса недостаточно, чтобы однозначно идентифицировать часовой пояс.
В качестве расширения ответа Джо Шоу синтаксический анализатор dateutil предлагает возможность предоставить сопоставление сокращений имен часовых поясов с объектами часовых поясов, полученными из имен часовых поясов IANA.
import dateutil
tzdict = {'EST': dateutil.tz.gettz('America/New_York'),
'EDT': dateutil.tz.gettz('America/New_York')}
dt = dateutil.parser.parse("Tue Jun 22 07:46:22 EST 2010", tzinfos=tzdict)
print(dt)
# 2010-06-22 07:46:22-04:00
print(repr(dt))
# datetime.datetime(2010, 6, 22, 7, 46, 22, tzinfo=tzfile('US/Eastern'))
Преимущество перед фиксированным смещением UTC состоит в том, что правила часовых поясов (например, переходы на летнее время) будут приняты во внимание, если вы выполните любую арифметику timedelta с полученным объектом datetime.
Попробуй решить эту точную проблему.
Что я в итоге сделал:
# starting with date string
sdt = "20190901"
std_format = '%Y%m%d'
# create naive datetime object
from datetime import datetime
dt = datetime.strptime(sdt, sdt_format)
# extract the relevant date time items
dt_formatters = ['%Y','%m','%d']
dt_vals = tuple(map(lambda formatter: int(datetime.strftime(dt,formatter)), dt_formatters))
# set timezone
import pendulum
tz = pendulum.timezone('utc')
dt_tz = datetime(*dt_vals,tzinfo=tz)