如何编写Python函数实现datetime对象的指定月份偏移?
问题
如何编写一个Python函数,接收datetime对象和整数作为输入,返回偏移指定月份后的新datetime对象?
我此前尝试了如下代码:
from datetime import datetime, timedelta def get_offset_datetime(source_date_time, month_offset): year, month, day = (source_date_time.year + month_offset // 12, source_date_time.month + month_offset % 12, source_date_time.day) if month>12: year+=1 month-=12 elif month<1: year-=1 month+=12 offset_datetime = datetime(year, month, day, source_date_time.hour, source_date_time.minute, source_date_time.second) return offset_datetime
但部分日期会报错,例如执行以下测试代码:
source_date_time = datetime(2022,1,31) month_offset = 1 offset_datetime = get_offset_datetime(source_date_time, month_offset) print(source_date_time) print(offset_datetime)
期望输出为:
2022-02-28 00:00:00
但实际出现如下错误:
Traceback (most recent call last): File "/home/user/Desktop/exam/main.py", line 42, in <module> offset_datetime = get_offset_datetime2(source_date_time, month_offset) File "/home/user/Desktop/exam/main.py", line 27, in get_offset_datetime2 offset_datetime = datetime(year, month, day, source_date_time.hour, source_date_time.minute, source_date_time.second) ValueError: day is out of range for month
解决方案
方法一:使用第三方库 python-dateutil
python-dateutil 的 relativedelta 专门处理这类日期偏移问题,能自动识别月末日期的边界情况,无需手动处理:
先安装依赖库:
pip install python-dateutil
编写函数:
from datetime import datetime from dateutil.relativedelta import relativedelta def get_offset_datetime(source_date_time, month_offset): return source_date_time + relativedelta(months=month_offset)
测试示例:
source_date_time = datetime(2022,1,31) month_offset = 1 offset_datetime = get_offset_datetime(source_date_time, month_offset) print(offset_datetime) # 输出 2022-02-28 00:00:00
方法二:纯标准库实现
如果不想依赖第三方库,可以通过计算目标月份的最后一天来规避日期越界问题:
from datetime import datetime, timedelta def get_offset_datetime(source_date_time, month_offset): # 计算目标年份和月份 target_year = source_date_time.year + month_offset // 12 target_month = source_date_time.month + month_offset % 12 # 调整跨年度的月份 if target_month > 12: target_year += 1 target_month -= 12 elif target_month < 1: target_year -= 1 target_month += 12 # 获取目标月份的最后一天:取下个月第一天再减1天 try: next_month_first = datetime(target_year, target_month + 1, 1) except ValueError: # 处理12月的情况,下个月是次年1月 next_month_first = datetime(target_year + 1, 1, 1) target_last_day = next_month_first - timedelta(days=1) # 取原日期日数和目标月最后一天的较小值,避免越界 target_day = min(source_date_time.day, target_last_day.day) # 返回新的datetime对象 return datetime( target_year, target_month, target_day, source_date_time.hour, source_date_time.minute, source_date_time.second )
测试多场景:
# 原测试用例 source_date_time = datetime(2022,1,31) month_offset = 1 print(get_offset_datetime(source_date_time, month_offset)) # 2022-02-28 00:00:00 # 闰年2月29日偏移 source_date_time = datetime(2020,2,29) month_offset = 1 print(get_offset_datetime(source_date_time, month_offset)) # 2020-03-29 00:00:00 # 反向偏移月份 source_date_time = datetime(2022,3,31) month_offset = -1 print(get_offset_datetime(source_date_time, month_offset)) # 2022-02-28 00:00:00
内容的提问来源于stack exchange,提问作者Hossein Vejdani
相关产品推荐
相关产品推荐

