【问题标题】:pytz timezone has the wrong offset during daylight saving time [duplicate]pytz 时区在夏令时的偏移量错误[重复]
【发布时间】:2020-07-16 16:02:53
【问题描述】:

我对 pytz 和夏令时有疑问。当我使用时区Europe/Berlin 时,它总是使用没有夏令时的时区偏移量。

小例子:

print(repr(pytz.timezone("Europe/Berlin")))
<DstTzInfo 'Europe/Berlin' CET+1:00:00 STD>
# Should probably be something like <DstTzInfo 'Europe/Berlin' CET+2:00:00 DST>

# Usage
from django.utils import timezone
from datetime import datetime
datetime_now = timezone.now()
print(my_time)
# Result: 00:00:00
print(datetime.combine(datetime_now, my_time, tzinfo=timezone.get_current_timezone()))
# Result: 2020-04-04 00:00:00+01:00, should be 2020-04-04 00:00:00+02:00

我的用例的最小示例是闹钟。用户将时钟设置为06:00(不考虑时区),时钟应在当前时区的06:00 响起,即在夏令时时为06:00+02,否则为Europe/Berlin 时为06:00+01


实现是一个 Django 模型,使用 django.models.TimeField 表示非感知时间(例如 06:00),我想通过创建一个包含当前日期和时间存储在TimeField

我愿意接受有关时间对象的不同建议(例如使用或不使用django.utils.timezone),只要我可以创建可以相互比较的日期时间对象并使用 timedelta 对象(或一些类似方法)递增/递减.


另一个最小的例子(Django 仅用于获取当前时区):

from django.utils import timezone
import datetime

tz = timezone.get_current_timezone()
time_now = datetime.datetime.now(tz=tz)
clock_time = datetime.time(1,2)
combined_time = datetime.combine(time_now, clock_time, tzinfo=tz)
print(tz)
print(time)
print(time_now)
print(combined_time)

结果

Europe/Berlin
01:02:00
2020-04-12 18:50:11.934754+02:00
2020-04-12 01:02:00+01:00

【问题讨论】:

    标签: python timezone pytz


    【解决方案1】:

    在构建时区感知日期时间时避免使用tzinfo。见this post

    由于您使用的是 Django,假设TIME_ZONE = 'Europe/Berlin',我们可以使用make_aware

    from django.utils import timezone
    from datetime import datetime, time
    
    # Get a localized datetime so that .combine gets the local date
    local_now = timezone.localtime()
    # localtime() is a shortcut for
    # timezone.now().astimezone(timezone.get_current_timezone())
    
    clock_time = time(1, 2)
    combined_time = timezone.make_aware(datetime.combine(local_now, clock_time))
    print(combined_time)
    

    它会打印出来

    2020-04-21 01:02:00+02:00
    

    或者,使用localize function in pytz(无论如何都在make_aware function definition中使用,但请查看下面的详细信息):

    tz = timezone.get_current_timezone()  # or pytz.timezone('Europe/Berlin')
    combined_time = tz.localize(datetime.combine(local_now, clock_time))
    # 2020-04-21 01:02:00+02:00
    

    如果你看到Django code for timezone.py,这些函数基本上都是 pytz 包装器。特别要检查make_awarelocaltimenow 的定义。

    不过,make_awarelocalize 之间有一个特别的区别。两者都接受参数is_dst,但对于Django 的make_aware,默认为None,而False for pytz如果用户在输入 DST 时写入的时间不存在或发生两次,则这种差异对您的情况很重要。在这里,拥有is_dst=None 将使函数分别提升NonExistentTimeErrorAmbiguousTimeError。否则,a boolean value will cause it to guess


    示例: 在今年的Europe/Berlin 中,时钟在 3 月 29 日凌晨 2:00 向前拨快了一小时。因此,当地时间凌晨 2:30 并没有发生。 Python 根据is_dst 处理这个输入:

    time_doesnt_exist = datetime(2020, 3, 29, 2, 30, 0)
    print(tz.localize(time_doesnt_exist, is_dst=None))
    # Raises NonExistentTimeError
    print(tz.localize(time_doesnt_exist, is_dst=True))
    2020-03-29 02:30:00+02:00
    print(tz.localize(time_doesnt_exist, is_dst=False))
    2020-03-29 02:30:00+01:00
    

    使用localize 获得异常引发行为:

    combined_time = tz.localize(datetime.combine(local_now, clock_time), is_dst=None)
    

    make_aware 改为不引发:

    combined_time = timezone.make_aware(
      datetime.combine(local_now, clock_time),
      is_dst=False,  # Or True...
    )
    

    注意事项:本地时间的算术

    对本地化日期时间进行算术运算需要 calling normalize 作为 DST 问题出现时的解决方法

    time_before_dst = datetime(2020, 3, 29, 1, 50, 0)
    local_time_before_dst = tz.localize(time_before_dst) 
    new_time = local_time_before_dst + timedelta(minutes=40)
    print(new_time)
    # 2020-03-29 02:30:00+01:00
    # Didn't switch to DST!
    print(tz.normalize(new_time))
    # 2020-03-29 03:30:00+02:00
    # Correctly did the switch
    

    【讨论】:

    • 对于localize 变体使用localize 使用tz.localize(datetime.combine(my_datetime_obj, time(1, 2))) 是否正确after combine(因为combine 不支持时区)?也许您可以在帖子中添加一个完整示例,说明如何以最佳方式使用它。
    • @allo 实际上是不对的。本地化需要在combine 之前进行,以便将时钟时间插入正确的日期。刚刚修复它并添加了有关 DST 处理的详细信息
    猜你喜欢
    • 2013-08-10
    • 2017-03-26
    • 2014-08-14
    • 2021-11-30
    • 2016-11-25
    • 1970-01-01
    • 2018-02-12
    • 2014-05-06
    • 1970-01-01
    相关资源
    最近更新 更多