Поле временной зоны в изоформате

У меня есть метка времени, которая должна быть в EST:

2014-10-06T18:06:40-04:56

Я понимаю эту первую часть: 2014-10-06T18:06:40, но не -04:56.

Что означает -04:56?

Вот как я получил эту метку времени:

import datetime
start_time = datetime.datetime(year  = 2014, 
                               month = 10, 
                               day   = 6, 
                               hour  = 18, 
                               tzinfo = pytz.timezone('US/Eastern'))
end_time   = start_time + datetime.timedelta(seconds=400)

И затем:

end_time.isoformat()

возвращает:

2014-10-06T18:06:40-04:56

Ответ 1

Проблема заключается в том, что pytz:

... отличается от документированного API Python для реализации tzinfo; если вы хотите создать локальные временные часы, вам нужно использовать метод localize(), задокументированный в этом документе...

Далее, он говорит:

К сожалению, использование аргумента tzinfo стандартных конструкторов datetime "не работает" с pytz для многих часовых поясов.

>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=amsterdam).strftime(fmt)
'2002-10-27 12:00:00 LMT+0020'

Итак, вам нужно сделать то, что предлагают документы, используя normalize, создавая время UTC и используя astimezone и т.д. Какой из них вы хотите, зависит от того, что вы пытаетесь сделать. Например:

>>> from datetime import datetime
>>> from pytz import timezone
>>> utc = timezone('UTC')
>>> eastern = timezone('US/Eastern')
>>> datetime(2014, 10, 6, 18, tzinfo=eastern).isoformat()
'2014-10-06T18:00:00-04:56'
>>> eastern.normalize(datetime(2014, 10, 6, 18, tzinfo=eastern)).isoformat()
'2014-10-06T18:56:00-04:00'
>>> datetime(2014, 10, 6, 18, tzinfo=utc).astimezone(eastern).isoformat()
'2014-10-06T14:00:00-04:00'
>>> eastern.localize(datetime(2014, 10, 6, 18)).isoformat()
'2014-10-06T18:00:00-04:00'

Я думаю, это последнее, чего ты хочешь. Как говорится в документах для localize:

Преобразование наивного времени в локальное время.

Этот метод следует использовать для построения локальных чем передача аргумента tzinfo в конструктор datetime.

И я думаю, что создание локального времени - это именно то, что вы хотели здесь.


Если вам интересно, почему... ну, если вы посмотрите на данные, которые находятся в вашей базе данных Olson, или просто распечатайте eastern._utcoffset, вы увидите -1 день, +68640 минут. Это 19.0166 + часов, а не 19. Почему? Потому что каждый часовой пояс определяется с его начальным смещением, с настройками оттуда. Восток основан на нью-йоркском часовом поясе с 1883 по 18 ноября 12:03:58, после чего он был -04: 56: 02 от GMT. Там корректировка дат начала в 1920 году, которая вычитает дополнительные 00:03:58. И, конечно, ежегодные корректировки взад и вперед на один час для DST. Итак, по состоянию на данный момент, восток - -04: 00, но без какой-либо идеи о том, какую дату он должен представлять, это -04: 56. И поскольку datetime просто запрашивает часовой пояс для своего смещения, а не его смещение в определенное время, то, что он получает.


Последнее: EST - Восточное стандартное время, которое составляет -05: 00. Это не часовой пояс в любом месте в США 6 октября 2014 года, потому что в 2014 году летнее время США сократится до 2 ноября. (Раньше были штаты в Индиане, которые были в EST летом, но их больше нет.) То, что вы ищете, - EDT, Eastern Daylight Time, которое составляет -04: 00. Или, конечно, ET, который является EDT в течение лета и EST в течение зимы, это то, что вы получаете от поиска 'US/Eastern' или 'America/New_York'.

Ответ 2

Что здесь означает -04: 56? `

Это означает, что код, который генерирует временную метку ввода, разбит таким образом, что демонстрирует отсутствие понимания того, как работают часы t20. Вы не должны доверять его результатам, если вы считаете, что только смещение UTC (-04:56) неверно, но сама дата является правильным временем в Восточном часовом поясе, а затем для разбора времени, do:

#!/usr/bin/env python
from datetime import datetime, timedelta
import pytz

tz = pytz.timezone('America/New_York')

naive = datetime.strptime("2014-10-06T18:06:40-04:56"[:-6],
                          "%Y-%m-%dT%H:%M:%S")
start_time = tz.localize(naive, is_dst=None)
end_time = tz.normalize(start_time + timedelta(seconds=400))
print(start_time.isoformat())
print(end_time.isoformat())
  • вы должны использовать tz.localize() вместо назначения атрибута tzinfo непосредственно
  • is_dst=None утверждает, что время ввода существует, и оно однозначно
  • tz.normalize() необходимо, если арифметика даты пересекает границы DST

Выход

2014-10-06T18:06:40-04:00
2014-10-06T18:13:20-04:00

Почему вам нужно localize(), normalize() описано в pytz docs (часть localize()/normalize() - это самое первое примечание в документации).

Почему -04:56 Смещение UTC ошибочно в 2014 в Восточном часовом поясе

Смещение UTC в одном и том же месте может быть разным в разное время из-за переходов DST или по другим причинам (например, война или потому, что какой-то политик считает, что это хорошая идея), например, здесь возможные значения для US/Eastern:

>>> import pytz
>>> pytz.timezone('US/Eastern')
{(datetime.timedelta(-1, 72000),
  datetime.timedelta(0, 3600),
  'EWT'): <DstTzInfo 'US/Eastern' EWT-1 day, 20:00:00 DST>,
 (datetime.timedelta(-1, 68640),
  datetime.timedelta(0),
  'LMT'): <DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>,
 (datetime.timedelta(-1, 72000),
  datetime.timedelta(0, 3600),
  'EDT'): <DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>,
 (datetime.timedelta(-1, 68400),
  datetime.timedelta(0),
  'EST'): <DstTzInfo 'US/Eastern' EST-1 day, 19:00:00 STD>,
 (datetime.timedelta(-1, 72000),
  datetime.timedelta(0, 3600),
  'EPT'): <DstTzInfo 'US/Eastern' EPT-1 day, 20:00:00 DST>}

Обратите внимание на смещение UTC для LMT tzinfo: timedelta(-1, 68640) == '-4:56:00'. Способ получить это - использовать неправильный код:

#XXX BROKEN, DO NOT DO IT
>>> dt = datetime(2014, 10, 6, tzinfo=pytz.timezone('US/Eastern'))
>>> dt
datetime.datetime(2014, 10, 6, 0, 0, tzinfo=<DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>)
>>> dt.isoformat()
2014-10-06T00:00:00-04:56

Присвоение tzinfo напрямую не позволяет pytz выбирать правильный tzinfo за данный момент времени, а вместо этого используется случайный объект tzinfo среди доступных. Вы всегда должны использовать tz.localize() для добавления правильной информации о часовом поясе.

Ответ 3

Предлагаю посмотреть arrow. Вышеупомянутые ответы сработали, но сделать код действительно запутанным. Стрелка позволяет сделать это просто используя:

In [82]: start_time = arrow.get(2014, 10, 6, 18).to('US/Eastern')
In [83]: end_time = start_time.shift(seconds=400)
In [84]: start_time
Out[84]: <Arrow [2014-10-06T14:00:00-04:00]>
In [85]: end_time
Out[85]: <Arrow [2014-10-06T14:06:40-04:00]>

Вы можете получить объекты datetime, используя

In [86]: start_time.datetime
Out[86]: datetime.datetime(2014, 10, 6, 14, 0, tzinfo=tzfile('/usr/share/zoneinfo/US/Eastern'))