Почему этот код зависит от часового пояса моей локальной машины?

Почему этот код:

def parse_date(datetime_string, tz_code):
    tz = timezone(tz_code)
    datetime_obj = parser.parse(datetime_string)
    datetime_obj_localized = datetime_obj.replace(tzinfo=tz)
    return time.mktime(datetime_obj_localized.timetuple())


def test_parse_date(self):
   self.assertEquals(1482951600, parse_date('2016-12-28 14:00', 'US/Eastern')

возвращает другое значение в зависимости от часового пояса машины, на которой он работает?

Насколько я понимаю, синтаксический анализатор возвращает дату и время без часового пояса, затем присваивает tz, ничего не меняя, и, наконец, он преобразуется в метку времени. Мой местный tz нигде не должен использоваться.


person Alain    schedule 16.10.2016    source источник


Ответы (1)


dateutil.parser добавит часовой пояс тогда и только тогда, когда он его найдет, что может зависеть от настроек часового пояса вашего локального компьютера, потому что, если он обнаружит сокращенный часовой пояс, такой как "EST", который находится в списке аббревиатуры для вашего местного часового пояса, он будет считать, что это тот, который вы имеете в виду.

Тем не менее, это не то, что происходит в этом случае. Даже если parser прикреплял часовой пояс, datetime.replace выполняет полную замену часового пояса, а не преобразование. Я не думаю, что в вашем вопросе достаточно информации для полной диагностики этой проблемы, но если timezone() равно pytz.timezone(), по крайней мере одна из ваших проблем заключается в том, что pytz часовые пояса не могут быть привязаны к datetime объектам с replace. Вместо этого вам нужно будет использовать datetime_obj = pytz.timezone(tz_code).localize(datetime_obj).

Обратите внимание, что метод localize предполагает, что у вас есть объект, не зависящий от часового пояса, поэтому он выдаст ошибку, если синтаксический анализатор datetutil вернет datetime с учетом часового пояса, поэтому вы должны передать ignoretz=True синтаксическому анализатору, чтобы предотвратить это.

Тем не менее, даже после того, как вы это сделаете, реальная проблема заключается в том, как вы используете time.mktime. См. этот ответ, который точно такая же проблема. Подводя итог этому ответу, лучше всего использовать calendar.timegm вместо mktime и преобразовать в UTC перед его вызовом. Включая мои предложения, вот обновленная версия вашего кода:

import calendar
from dateutil.tz import tzutc

def parse_date(datetime_string, tz_code):
    tz = timezone(tz_code)
    datetime_obj = parser.parse(datetime_string, ignoretz=True)
    datetime_obj_localized = tz.localize(datetime_obj)

    datetime_obj_utc = datetime_obj_localized.astimezone(tzutc())
    return calendar.timegm(datetime_obj_utc.timetuple())
person Paul    schedule 16.10.2016
comment
попробовал ваш исправленный код и все еще не работает, если мой местный tz не является американским/восточным. Вы сказали: вместо этого вам нужно будет использовать datetime_obj = pytz.timezone(tz_code).localize(datetime_obj), но это то, что я делаю в строке № 4... Увы, часовой пояс взят из pytz, так что я думаю, проблема в этом. . - person Alain; 16.10.2016
comment
@ Ален Нет, в строке 4 вы используете datetime_obj.replace(tz=pytz.timezone(tz_code)). pytz не поддерживает стандартный интерфейс привязки часовых поясов. - person Paul; 16.10.2016
comment
@Alain Ален, я обновил свой ответ, я считаю, что настоящая проблема в том, что mktime игнорирует часовой пояс. - person Paul; 16.10.2016
comment
Собираюсь быть афк в течение нескольких часов. Я заменяю mktime на int(datetime_obj_localized.strftime('%s')) и все равно не работает. - person Alain; 16.10.2016
comment
упс, не пробовал обновленный ответ (страница SO не обновлялась). Пойду, когда вернусь домой. спасибо - person Alain; 16.10.2016
comment
Это сработало! Спасибо, многому научился из вашего ответа и связанного вопроса. - person Alain; 17.10.2016