Есть ли простой способ получить текущую разницу во времени между текущим часовым поясом и UTC в Python?

Моя цель - просто вывести строку часового пояса, например -0700, используемую в электронной почте. В частности, я хочу, чтобы он отражал точный часовой пояс, включая переход на летнее время. Это означает, что я получу -07:00 в Калифорнии летом, -08:00 в Калифорнии зимой и всегда +08:00 в Китае (без перехода на летнее время).

Мне трудно по двум причинам:

  1. Целочисленное деление Python, в отличие от C99 и обычных реализаций C/C++, всегда округляется до нуля, а не до нуля. (Это имеет значение для расчетов с часовым поясом, например -xx30).
  2. struct_time.tm_isdst и time.daylight не отражают, действует ли переход на летнее время.

В итоге я получил код, подобный следующему. Это длиннее, чем я ожидал (особенно для Python!):

import math
import time

def my_divide(dividend, divisor):
    if dividend < 0:
        if divisor < 0:
            diff_sign = False
        else:
            diff_sign = True
    else:
        if divisor >= 0:
            diff_sign = False
        else:
            diff_sign = True
    if diff_sign:
        return (int(math.ceil(1.0 * dividend / divisor)), dividend % -divisor)
    else:
        return (int(math.floor(1.0 * dividend / divisor)), dividend % divisor)

def my_timezone():
    gt = time.gmtime()
    lt = time.localtime()
    lt_write = list(lt)
    lt_write[8] = 0  # is_dst
    lt = time.struct_time(lt_write)
    seconds = time.mktime(lt) - time.mktime(gt)
    (hours, minutes) = my_divide(seconds, 3600)
    minutes = abs(minutes) // 60
    return '%(hours)+03d%(minutes)02d' % {'hours': hours, 'minutes': minutes}

У кого-нибудь есть лучшая реализация?


person Yongwei Wu    schedule 10.10.2013    source источник
comment
используйте datetime и pytz pytz.sourceforge.net   -  person monkut    schedule 10.10.2013
comment
@monkut: для просто текущего смещения часового пояса машины это излишество.   -  person Martijn Pieters    schedule 10.10.2013
comment
@Martjin: я думаю, что допустил ошибку при тестировании struct_time. На самом деле это решение, которое я предлагаю сейчас.   -  person Yongwei Wu    schedule 10.10.2013
comment
спасибо, я не знал, что time знал о dst.   -  person monkut    schedule 11.10.2013


Ответы (1)


Вы можете использовать time.timezone и time.altzone.

Они обеспечивают:

Смещение местного (не летнего) часового пояса в секундах к западу от UTC (отрицательное значение в большинстве стран Западной Европы, положительное значение в США, ноль в Великобритании)

а также

Смещение местного часового пояса летнего времени в секундах к западу от UTC, если оно определено. Это отрицательно, если местный часовой пояс DST находится восточнее UTC (как в Западной Европе, включая Великобританию). Используйте это, только если daylight не равно нулю.

Как поясняется в документации по time.altzone, используйте последний вариант, когда time.daylight ненулевое, но только если текущее время летнее:

import time

def my_timezone():
    is_dst = time.daylight and time.localtime().tm_isdst
    offset = time.altzone if is_dst else time.timezone
    westerly = offset > 0
    minutes, seconds = divmod(abs(offset), 60)
    hours, minutes = divmod(minutes, 60)
    return '{}{:02d}{:02d}'.format('-' if westerly else '+', hours, minutes)

Используя секунды в качестве основы и divmod(), здесь не должно быть ошибок округления.

Для моего британского летнего местоположения это дает:

>>> my_timezone()
'+0100'
person Martijn Pieters    schedule 10.10.2013
comment
time.daylight — это проблема (унаследованная от C). Он не меняется в течение года. т.е. вы получите +0100 даже зимой. Но все равно спасибо. - person Yongwei Wu; 10.10.2013
comment
Если вы замените time.daylight на time.localtime().tm_isdst, ваш метод действительно сработает. Не могли бы вы обновить свой ответ, чтобы я мог его отметить? - person Yongwei Wu; 10.10.2013
comment
@Martjin: в соответствии с Единой спецификацией Unix функция tzset() также устанавливает для внешней переменной дневной свет значение 0, если преобразование летнего времени никогда не должно применяться для используемого часового пояса; иначе ненулевое. pubs.opengroup.org/onlinepubs/7908799/xsh/tzset.html - person Yongwei Wu; 10.10.2013
comment
@Martjin: Значит, мы согласны? На самом деле я утверждаю, что мы должны использовать struct_time.tm_isdst, но не использовать time.daylight. Ваш код в настоящее время использует последний. - person Yongwei Wu; 10.10.2013
comment
@YongweiWu: и теперь мы подошли к тому, в чем заключается путаница, и оказывается, что я ошибался в этом вопросе. :-D - person Martijn Pieters; 10.10.2013
comment
@YongweiWu: действительно, time.daylight — это флаг, указывающий, что часовой пояс поддерживает летнее время, а не в том случае, если летнее время активно прямо сейчас. Будет корректировать. - person Martijn Pieters; 10.10.2013
comment
@YongweiWu: еще раз приношу свои извинения; Я упустил важную деталь и исправил предположение. Правильный способ определить, следует ли использовать time.altzone, — это проверить как time.daylight, так и time.localtime().tm_isdst. - person Martijn Pieters; 10.10.2013
comment
Хороший. Я отметил ваш ответ и удалил некоторые комментарии, которые больше не актуальны. - person Yongwei Wu; 10.10.2013
comment
Ваш ответ по-прежнему ссылается на time.daylight, что может сбивать с толку. Подумайте о том, чтобы пересмотреть это тоже. :-) - person Yongwei Wu; 10.10.2013
comment
@YongweiWu: ну, его использование правильное; вам нужно только посмотреть на tm_isdst, если time.daylight не равно нулю. Он избавляет вас от системного вызова, сначала проверяя, нужен ли вообще системный вызов. - person Martijn Pieters; 10.10.2013
comment
@Martjin: Интересное примечание. Я предполагаю, что разницу в производительности трудно заметить, а код стал немного более подробным. Хорошо еще знать твое намерение. - person Yongwei Wu; 10.10.2013