You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

考虑公历月份天数差异的月份增减及历史日期列表生成问题

前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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 19:36:03