如何生成当前日期前n个月份的月末日期列表(不含当月)
生成当前日期前N个月份的月末日期列表(不含当月)
需求说明
需要生成当前日期之前n个月份的月末日期列表,不包含当月的月末日期。例如,若当前为2022年12月,过去4个月的月末日期应为:
daterange = ['2022-08-31','2022-09-30','2022-10-31','2022-11-30']
尝试过的方法及问题
方法1:Python自定义函数
编写了如下函数,用固定30天近似计算:
def last_n_month_end(n_months): """ Returns a list of the last n month end dates """ return [datetime.date.today().replace(day=1) - datetime.timedelta(days=1) - datetime.timedelta(days=30*i) for i in range(n_months)]
- 问题:仅在每月均为30天时部分有效,无法适配不同月份的天数差异;
- 在Databricks PySpark环境中运行时抛出错误:
AttributeError: 'method_descriptor' object has no attribute 'today'(因Databricks中datetime模块的使用方式差异导致)。
方法2:Stack Overflow方案
尝试了以下代码,但未得到正确结果:
def previous_month_ends(date, months): year, month, day = [int(x) for x in date.split('-')] d = datetime.date(year, month, day) t = datetime.timedelta(1) s = datetime.date(year, month, 1) return [(x - t).strftime('%Y-%m-%d') for m in range(months - 1, -1, -1) for x in (datetime.date(s.year, s.month - m, s.day) if s.month > m else \ datetime.date(s.year - 1, s.month - (m - 12), s.day),)]
方法3:PySpark代码
尝试用PySpark的sequence函数生成,但生成过去3个月的月末日期时,所有日期均为30日(例如10月实际月末为31日),仅当生成超过3个月时结果正确:
df = spark.createDataFrame([(1,)],['id']) days = df.withColumn('last_dates', explode(expr('sequence(last_day(add_months(current_date(),-3)), last_day(add_months(current_date(), -1)), interval 1 month)')))
正确解决方案
方案1:Python环境(含Databricks)
使用dateutil.relativedelta处理月份偏移,准确计算每个月的月末:
from datetime import datetime from dateutil.relativedelta import relativedelta def get_last_n_month_ends(n_months): # 获取上月月末日期 current_last_day = datetime.today().replace(day=1) - relativedelta(days=1) result = [] for i in range(n_months): # 逐月往前偏移,取月末日期 month_end = current_last_day - relativedelta(months=i) result.append(month_end.strftime('%Y-%m-%d')) # 反转列表让日期从旧到新排列(可根据需求调整顺序) return result[::-1] # 示例:获取过去4个月的月末日期 print(get_last_n_month_ends(4)) # 输出(假设当前为2022年12月):['2022-08-31', '2022-09-30', '2022-10-31', '2022-11-30']
若Databricks中缺少dateutil,可通过%pip install python-dateutil命令安装。
方案2:PySpark环境
调整序列生成逻辑,先获取每月第一天再往前推1天得到月末,避免interval偏移导致的日期错误:
from pyspark.sql import functions as F def get_spark_month_ends(n_months): # 生成从当前月往前n+1个月到上月的第一天序列 start_date = F.add_months(F.trunc(F.current_date(), "month"), -(n_months)) end_date = F.add_months(F.trunc(F.current_date(), "month"), -1) df = spark.createDataFrame([(1,)], ["id"]) df = df.withColumn( "month_starts", F.explode(F.sequence(start_date, end_date, F.expr("interval 1 month"))) ).withColumn( "month_ends", F.date_sub(F.col("month_starts"), 1) ).select("month_ends") # 转换为列表格式 return [row[0] for row in df.collect()] # 示例:获取过去4个月的月末日期 print(get_spark_month_ends(4)) # 输出(假设当前为2022年12月):['2022-08-31', '2022-09-30', '2022-10-31', '2022-11-30']
该方法通过每月第一天减1天的方式,确保得到准确的月末日期,不会出现固定30日的错误。
内容的提问来源于stack exchange,提问作者doubleD
相关产品推荐
相关产品推荐

