Home > Mobile >  write a function that generates a list of last days of each month for the past n months from current
write a function that generates a list of last days of each month for the past n months from current

Time:12-03

I am trying to create a list of the last days of each month for the past n months from the current date but not including current month

I tried different approaches:

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)]

somehow this partly works if each every month only has 30 days and also not work in databricks pyspark. It returns AttributeError: 'method_descriptor' object has no attribute 'today'

I also tried the approach mentioned in Generate a sequence of the last days of all previous N months with a given month

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),)]

but I am not getting it correctly.

I also tried:

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)')))

I got the last three months (Sep, oct, nov), but all of them are the 30th but Oct has Oct 31st. However, it gives me the correct last days when I put more than 3.

What I am trying to get is this: (last days of the last 4 months not including last_day of current_date)

daterange = ['2022-08-31','2022-09-30','2022-10-31','2022-11-30']

CodePudding user response:

Not sure if this is the best or optimal way to do it, but this does it...

Requires the following package since datetime does not seem to have anyway to subtract months as far as I know without hardcoding the number of days or weeks. Not sure, so don't quote me on this....

Package Installation:

pip install python-dateutil

Edit: There was a misunderstanding from my end. I had assumed that all dates were required and not just the month ends. Anyways hope the updated code might help. Still not the most optimal, but easy to understand I guess..

# import datetime package
from datetime import date, timedelta
from dateutil.relativedelta import relativedelta


def previous_month_ends(months_to_subtract):
    # get first day of current month
    first_day_of_current_month = date.today().replace(day=1)
    print(f"First Day of Current Month: {first_day_of_current_month}")
    # Calculate and previous month's Last date
    date_range_list = [first_day_of_current_month - relativedelta(days=1)]
    cur_iter = 1
    while cur_iter < months_to_subtract:
        # Calculate First Day of previous months relative to first day of current month
        cur_iter_fdom = first_day_of_current_month - relativedelta(months=cur_iter)
        # Subtract one day to get the last day of previous month
        cur_iter_ldom = cur_iter_fdom - relativedelta(days=1)
        # Append to the list
        date_range_list.append(cur_iter_ldom)
        # Increment Counter
        cur_iter =1
    return date_range_list

print(previous_month_ends(3))

Function to calculate date list between 2 dates:

  1. Calculate the first of current month.
  2. Calculate start and end dates and then loop through them to get the list of dates.
  3. I have ignored the date argument, since I have assumed that it will be for current date. alternatively it can be added following your own code which should work perfectly.
# import datetime package
from datetime import date, timedelta
from dateutil.relativedelta import relativedelta


def gen_date_list(months_to_subtract):
    # get first day of current month
    first_day_of_current_month = date.today().replace(day=1)
    print(f"First Day of Current Month: {first_day_of_current_month}")
    start_date = first_day_of_current_month - relativedelta(months=months_to_subtract)
    end_date = first_day_of_current_month - relativedelta(days=1)
    print(f"Start Date: {start_date}")
    print(f"End Date: {end_date}")
    date_range_list = [start_date]
    cur_iter_date = start_date
    while cur_iter_date < end_date:
        cur_iter_date  = timedelta(days=1)
        date_range_list.append(cur_iter_date)
        # print(date_range_list)
    return date_range_list

print(gen_date_list(3))

Hope it helps...Edits/Comments are welcome - I am learning myself...

CodePudding user response:

from datetime import datetime, timedelta

def get_last_dates(n_months):
   '''
   generates a list of lastdates for each month for the past n months
   Param:
     n_months = number of months back
   '''
  last_dates = []  # initiate an empty list
  for i in range(n_months):
    last_dates.append((datetime.today() - timedelta(days=i*30)).replace(day=1) - timedelta(days=1))
  return last_dates

This should give you a more accurate last_days

CodePudding user response:

I just thought a work around I can use since my last codes work:

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)')))

is to enter -4 and just remove the last_date that I do not need days.pop(0) that should give me the list of needed last_dates.

  • Related