如何在Python(pytz)中以支持DST的方式为datetime添加一天?
在US/Eastern时区夏令时转换时正确实现日期日增迭代器
在处理US/Eastern时区的日期迭代时,遇到夏令时(DST)转换边界(2024年11月3日到4日)会出现问题:当地时间11月3日0点到11月5日0点间隔25小时,直接用datetime.timedelta(days=1)加24小时会得到不符合预期的结果,无法正确跳到下一天的当地时间。
比如原代码的问题:
import datetime import pytz ET = pytz.timezone("US/Eastern") first_day = ET.localize(datetime.datetime(2024, 11, 3)) next_day = first_day + datetime.timedelta(days=1) first_day.isoformat() # '2024-11-03T00:00:00-04:00' next_day.isoformat() # '2024-11-04T00:00:00-04:00' assert next_day == ET.localize(datetime.datetime(2024, 11, 4)) # 断言失败
使用ET.normalize(next_day)得到的是'2024-11-03T23:00:00-05:00',也不符合预期。
核心原因
timedelta(days=1)是按绝对时间增加24小时,但在DST结束时,US/Eastern时区的偏移从-04:00(EDT)切换到-05:00(EST),导致当地日历日的绝对时长变为25小时。直接加24小时只会走到下一日历日的前一小时,而非我们需要的“下一个日历日的同一当地时间”。
解决方案
方案1:基于日历日期递增(pytz兼容)
直接操作date对象而非带时区的datetime的绝对时间,先取当前日期加一天,再重新构造成带时区的datetime:
import datetime import pytz ET = pytz.timezone("US/Eastern") first_day = ET.localize(datetime.datetime(2024, 11, 3)) # 提取日期部分,加一天后重新本地化 next_calendar_date = first_day.date() + datetime.timedelta(days=1) next_day = ET.localize(datetime.datetime.combine(next_calendar_date, datetime.time.min)) print(first_day.isoformat()) # '2024-11-03T00:00:00-04:00' print(next_day.isoformat()) # '2024-11-04T00:00:00-05:00' assert next_day == ET.localize(datetime.datetime(2024, 11, 4)) # 断言通过
方案2:使用Python3.9+自带的zoneinfo(推荐)
zoneinfo是Python标准库的一部分,处理时区更直观,无需额外安装:
from zoneinfo import ZoneInfo import datetime ET = ZoneInfo("US/Eastern") first_day = datetime.datetime(2024, 11, 3, tzinfo=ET) # 同样基于日历日期递增 next_calendar_date = first_day.date() + datetime.timedelta(days=1) next_day = datetime.datetime.combine(next_calendar_date, datetime.time.min, tzinfo=ET) print(first_day.isoformat()) # '2024-11-03T00:00:00-04:00' print(next_day.isoformat()) # '2024-11-04T00:00:00-05:00' assert next_day == datetime.datetime(2024, 11, 4, tzinfo=ET) # 断言通过
迭代器实现
如果要封装成循环迭代器,可以这样写:
from zoneinfo import ZoneInfo import datetime def date_iterator(start_date, timezone, days=1): current = datetime.datetime.combine(start_date, datetime.time.min, tzinfo=timezone) while True: yield current current_date = current.date() + datetime.timedelta(days=days) current = datetime.datetime.combine(current_date, datetime.time.min, tzinfo=timezone) # 使用示例 ET = ZoneInfo("US/Eastern") start = datetime.date(2024, 11, 2) iterator = date_iterator(start, ET) # 迭代前3天 for _ in range(3): day = next(iterator) print(day.isoformat()) # 输出: # 2024-11-02T00:00:00-04:00 # 2024-11-03T00:00:00-04:00 # 2024-11-04T00:00:00-05:00
内容的提问来源于stack exchange,提问作者Pwnosaurus
相关产品推荐
相关产品推荐

