python - pytz 时区在夏令时的偏移量错误
问题描述
我对 pytz 和夏令时有疑问。当我使用 timezone 时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
),我想通过创建一个日期时间对象来将它与当前时间和其他时间字段进行比较,该对象具有存储在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
在构建时区感知日期时间时避免使用。看到这个帖子。
由于您使用的是 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
pytz中的函数(无论如何都在make_aware
函数定义中使用,但请查看下面的详细信息):
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
如果您看到timezone.py 的 Django 代码,这些函数基本上是 pytz 包装器。特别要检查 和make_aware
的localtime
定义now
。
make_aware
不过和之间有一个特别的区别localize
。两者都接受参数is_dst
,但对于 Django make_aware
,它是None
默认的,而它是False
用于 pytz的。如果用户在输入 DST 时写入的时间不存在或发生两次,则此差异对您的情况很重要。在这里,具有is_dst=None
将分别使函数 raiseNonExistentTimeError
或AmbiguousTimeError
。否则,布尔值会导致它猜测。
示例:
今年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...
)
提醒一句:本地化时间的算术
对本地化日期时间进行算术运算需要调用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
推荐阅读
- java - 应用程序在某些手机中停止,而在某些手机中正在运行
- mysql - Google Bigquery 查询记录数据 - 专利
- python - Flask 从 MYSQL 上传并显示图片
- sql - SQL 显示一起打得最多的球员的名字
- c# - C# XY图表,根据x值获取y值
- postgresql - 如何检查 ref 游标是否指向 postgres 中的空结果集
- docker - 当我尝试从 docker 容器中从谷歌驱动器下载文件时出现各种错误
- javascript - 反应 useEffect 检测是否找到记录
- outlook - 在 Outlook REST API 上调用“me/message/[itemId]”时经常收到 ErrorItemNotFound
- mongodb - Mongodb 和 Studio 3T sql