Python strptime () и часовые пояса?

157

У меня есть дамп-файл 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()поиграть с часовыми поясами?

victorhooi
источник
1
Разве вы не можете просто ... конвертировать все даты в GMT?
Робус
2
@Robus: Хм, я надеялся сделать это, но я предполагал, что strftime / datetime может как-то это сделать? В любом случае, мне нужно сохранить / проанализировать тот факт, что даты находятся в часовом поясе EST, или в каком часовом поясе они происходят со мной. Скрипт должен иметь возможность анализировать общие даты и время с информацией о часовом поясе (например, ETC может быть любым другим часовым поясом).
victorhooi
3
EST - также сокращение от часового пояса США. (Точно так же BST является аббревиатурой часового пояса Великобритании и Бразилии.) Такие аббревиатуры просто неоднозначны. Вместо этого используйте смещения относительно UTC / GMT. (Если вам нужно поддерживать сокращения, вам нужно сделать отображение зависимым от локали, и это грязная крысиная нора.)
Donal Fellows

Ответы:

58

Документация к 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
Джон Мачин
источник
13
Связанная ошибка Python: % Z в strptime не соответствует EST и другим
jfs
354

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

Джо Шоу
источник
Учитывая, что так много людей склонны использовать python-dateutil, я хотел бы указать нам на одно ограничение этой библиотеки. >>> parser.parse("Thu, 25 Sep 2003 10:49:41,123 -0300") Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Users/wanghq/awscli/lib/python2.7/site-packages/dateutil/parser.py", line 748, in parse return DEFAULTPARSER.parse(timestr, **kwargs) File "/Users/wanghq/awscli/lib/python2.7/site-packages/dateutil/parser.py", line 310, in parse res, skipped_tokens = self._parse(timestr, **kwargs) TypeError: 'NoneType' object is not iterable
wanghq
1
@wanghq вам нужно заменить последнюю запятую на точку. Затемparser.parse("Thu, 25 Sep 2003 10:49:41.123 -0300") returns: datetime.datetime(2003, 9, 25, 10, 49, 41, 123000, tzinfo=tzoffset(None, -10800))
flyfoxlee
7
@flyingfoxlee, да, я это понимаю. Я просто хочу рассказать людям об ограничении python-dateutil. Он делает магические вещи, но иногда не может этого сделать. Так что «просто назначьте свидание этому, и оно делает правильные вещи». не на 100% верно.
wanghq
4
dateutil.parser.parse("10-27-2016 09:06 AM PDT")возвращается: datetime.datetime(2016, 10, 27, 9, 6)не в состоянии определить часовой пояс ...
HaPsantran
2
Это зависит от цели. dateutil parserможет быть простым в использовании, но strptime()быстрее. Кроме того, его форматы довольно просты в освоении.
Восхищение
9

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

JFS
источник
0

Столкнулся с этой точной проблемой.

Что я в итоге сделал:

# 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)
Кристофер
источник