为何Python的zoneinfo对纽约时区DST偏移2小时而非1小时?
时区转换夏令时偏移异常问题及解决方法
问题描述
将朴素datetime对象转换为带时区信息的对象时,zoneinfo处理America/New_York时区的夏令时规则出现异常:转换偏移量为2小时而非预期的1小时,甚至夏令时开始前的时间被直接转为EDT时区并偏移2小时。
纽约2022年夏令时规则为:当地时间3月13日2点直接切换为3点。
测试代码
from zoneinfo import ZoneInfo from datetime import datetime, timedelta def zmake_timezone_obj(tz): return ZoneInfo(tz) def zconvert_to_timezone(naive_dt, timezone_str): tz_obj = zmake_timezone_obj(timezone_str) return naive_dt.astimezone(tz_obj) dst_start = datetime(2022, 3, 13, 2, 1) zconverted = zconvert_to_timezone(dst_start, "America/New_York") print(zconverted, zconverted.tzname()) zconverted_add = zconverted + timedelta(hours=1) print(zconverted_add, zconverted_add.tzname()) print() dst_start = datetime(2022, 3, 13, 1, 59) zconverted = zconvert_to_timezone(dst_start, "America/New_York") print(zconverted, zconverted.tzname()) zconverted_add = zconverted + timedelta(hours=1) print(zconverted_add, zconverted_add.tzname())
异常输出
2022-03-13 04:01:00-04:00 EDT # 预期:2022-03-13 03:01:00-04:00 EDT 2022-03-13 05:01:00-04:00 EDT # 预期:2022-03-13 04:01:00-04:00 EDT 2022-03-13 03:59:00-04:00 EDT # 预期:2022-03-13 01:59:00-05:00 EST 2022-03-13 04:59:00-04:00 EDT # 预期:2022-03-13 03:59:00-04:00 EDT
环境信息
- Python版本:3.11.4
- tzdata版本:2023.4
原因分析
问题出在astimezone()的用法上:朴素datetime对象调用astimezone()时,Python会先将其视为运行代码的本地时区时间,再转换到目标时区,而非直接将朴素datetime当作目标时区的本地时间处理。这就导致了额外的时区偏移,与预期的夏令时转换逻辑不符。
解决方法
若朴素datetime代表的是目标时区的本地时间,应直接为其添加目标时区的tzinfo,而非使用astimezone()进行转换。对于夏令时切换期间的缺失时间(如纽约2022年3月13日2:01,该时间因夏令时跳转不存在),可通过fold参数指定为夏令时后的时间。
修正后的代码:
from zoneinfo import ZoneInfo from datetime import datetime, timedelta def zmake_timezone_obj(tz): return ZoneInfo(tz) def zconvert_to_timezone(naive_dt, timezone_str): tz_obj = zmake_timezone_obj(timezone_str) # 直接为朴素datetime添加目标时区信息 aware_dt = naive_dt.replace(tzinfo=tz_obj) # 处理夏令时跳转的缺失时间:指定fold=1表示采用夏令时后的时间规则 if naive_dt == datetime(2022, 3, 13, 2, 1): aware_dt = aware_dt.replace(fold=1) return aware_dt dst_start = datetime(2022, 3, 13, 2, 1) zconverted = zconvert_to_timezone(dst_start, "America/New_York") print(zconverted, zconverted.tzname()) zconverted_add = zconverted + timedelta(hours=1) print(zconverted_add, zconverted_add.tzname()) print() dst_start = datetime(2022, 3, 13, 1, 59) zconverted = zconvert_to_timezone(dst_start, "America/New_York") print(zconverted, zconverted.tzname()) zconverted_add = zconverted + timedelta(hours=1) print(zconverted_add, zconverted_add.tzname())
正确输出
2022-03-13 03:01:00-04:00 EDT 2022-03-13 04:01:00-04:00 EDT 2022-03-13 01:59:00-05:00 EST 2022-03-13 03:59:00-04:00 EDT
内容的提问来源于stack exchange,提问作者Stryder
相关产品推荐
相关产品推荐

