pytz时区在夏令时有错误的偏移

2024-04-26 21:19:45 发布

您现在位置:Python中文网/ 问答频道 /正文

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

最简单的例子:

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处响起,即,当它是DST时06:00+02,否则06:00+01Europe/Berlin


该实现是一个Django模型,使用django.models.TimeField表示非感知时间(例如06:00),我希望通过创建一个日期时间对象,将其与当前时间和其他时间字段进行比较,该对象的当前日期和时间存储在TimeField

我愿意接受关于时间对象的不同建议(例如使用或不使用django.utils.timezone),只要我可以创建datetime对象,可以相互比较,并使用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

Tags: 对象djangofromimportdatetimetime时间utils
1条回答
网友
1楼 · 发布于 2024-04-26 21:19:45

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

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

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

或者,使用^{} function in pytz(无论如何,它在^{} 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,而它是^{} for pytz。如果用户在输入DST时写入了一个不存在的时间,或者写入了两次时间,则这种差异在您的情况下非常重要。这里,拥有is_dst=None将使函数分别提升NonExistentTimeErrorAmbiguousTimeError。否则,a boolean value will cause it to guess


示例: 今年3月29日凌晨2点,时钟向前拨了一个小时。因此,当地时间凌晨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 ^{}作为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

相关问题 更多 >