pytz 时区在夏令时期间有错误的偏移量
pytz timezone has the wrong offset during daylight saving time
我对 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
响铃,即 06:00+02
时为 DST,否则为 06:00+01
Europe/Berlin
.
该实现是一个 Django 模型,使用 django.models.TimeField
作为非感知时间(例如 06:00
),我想通过创建一个 datetime 对象将它与当前时间和其他 TimeFields 进行比较当前日期和时间存储在 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
在构建时区感知日期时间时避免使用 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 (which is used in the 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_aware
、localtime
和 now
.
的定义
不过 make_aware
和 localize
之间有一个特别的区别。两者都接受参数 is_dst
,但对于 Django 的 make_aware
默认情况下是 None
,而它是 False
for pytz. This difference matters in your case if a user writes a time that doesn't exist, or happens twice, when entering DST. Here, having is_dst=None
will make the function raise NonExistentTimeError
or AmbiguousTimeError
, respectively. Otherwise, a boolean value will cause it to guess.
示例:
今年 Europe/Berlin
,时钟在 3 月 29 日 2:00 上午拨快了一小时。因此,2:30am 不是当地时间。 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
我对 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
响铃,即 06:00+02
时为 DST,否则为 06:00+01
Europe/Berlin
.
该实现是一个 Django 模型,使用 django.models.TimeField
作为非感知时间(例如 06:00
),我想通过创建一个 datetime 对象将它与当前时间和其他 TimeFields 进行比较当前日期和时间存储在 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
在构建时区感知日期时间时避免使用 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 (which is used in the 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_aware
、localtime
和 now
.
不过 make_aware
和 localize
之间有一个特别的区别。两者都接受参数 is_dst
,但对于 Django 的 make_aware
默认情况下是 None
,而它是 False
for pytz. This difference matters in your case if a user writes a time that doesn't exist, or happens twice, when entering DST. Here, having is_dst=None
will make the function raise NonExistentTimeError
or AmbiguousTimeError
, respectively. Otherwise, a boolean value will cause it to guess.
示例:
今年 Europe/Berlin
,时钟在 3 月 29 日 2:00 上午拨快了一小时。因此,2:30am 不是当地时间。 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