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