考虑公历月份天数差异的月份增减及历史日期列表生成问题
前n个月日期生成月末对齐问题解决方案
问题描述
需要实现从给定日期dt出发,生成其前n个月对应日期列表的功能,但直接使用dateutil.relativedelta计算时,月末日期处理不符合预期,示例如下:
from datetime import datetime from dateutil.relativedelta import relativedelta # 简单案例 dt = datetime(2021, 2, 15) dt - relativedelta(months=1) # n=1输出datetime.datetime(2021, 1, 15, 0, 0) dt - relativedelta(months=2) # n=2输出datetime.datetime(2020, 12, 15, 0, 0) # 简单案例2 dt = datetime(2021, 3, 31) dt - relativedelta(months=1) # n=1输出datetime.datetime(2021, 2, 28, 0, 0) dt - relativedelta(months=2) # n=2输出datetime.datetime(2021, 1, 31, 0, 0) dt - relativedelta(months=3) # n=3输出datetime.datetime(2020, 12, 31, 0, 0) dt - relativedelta(months=4) # n=4输出datetime.datetime(2020, 11, 30, 0, 0) # 棘手案例3 dt = datetime(2021, 2, 28) dt - relativedelta(months=1) # n=1输出datetime.datetime(2021, 1, 28, 0, 0),而非预期的2021-01-31 dt - relativedelta(months=2) # n=2输出datetime.datetime(2020, 12, 28, 0, 0),而非预期的2020-12-31 dt - relativedelta(months=3) # n=3输出datetime.datetime(2020, 11, 28, 0, 0),而非预期的2020-11-30 dt - relativedelta(months=4) # n=4输出datetime.datetime(2020, 10, 28, 0, 0),而非预期的2020-10-31
原因说明
dateutil.relativedelta的默认计算逻辑为:先调整月份,再保留原日期的日数,仅当目标月没有对应日数时,才自动下调到目标月的最后一天。
对于案例3中的2021-02-28,因为1月、12月等都有28日,所以直接保留日数28,不会自动对齐到月末,不符合「输入为月末时输出所有前推日期都为对应月月末」的需求。
实现代码
from datetime import datetime, timedelta from dateutil.relativedelta import relativedelta def get_prev_n_month_dates(dt: datetime, n: int) -> list[datetime]: # 判定输入日期是否为当月最后一天 next_month_first_day = dt + relativedelta(months=1, day=1) is_month_end = (next_month_first_day - timedelta(days=1)).day == dt.day result = [] for i in range(1, n+1): current = dt - relativedelta(months=i) if is_month_end: # 输入为月末时,强制取目标月最后一天 current = current + relativedelta(day=31) result.append(current) return result
效果验证
测试棘手案例3:
dt = datetime(2021, 2, 28) print(get_prev_n_month_dates(dt, 4))
输出结果完全符合预期:
[datetime.datetime(2021, 1, 31, 0, 0), datetime.datetime(2020, 12, 31, 0, 0), datetime.datetime(2020, 11, 30, 0, 0), datetime.datetime(2020, 10, 31, 0, 0)]
其余普通场景、月末场景测试均和原始逻辑保持一致,无兼容性问题。
内容的提问来源于stack exchange,提问作者Gerry
相关产品推荐
相关产品推荐

