2

If I give a date in a string, e.g start_date = '01-Feb-21', how can I generate a total of eight dates from this one input?

Those eight dates being:

['01-Feb-21', # we get this from the input
 '28-Feb-21',
 '01-Jan-21',
 '31-Jan-21',
 '01-Dec-20',
 '31-Dec-20',
 '01-Nov-20',
 '30-Nov-20']

corresponding to the month starts and ends of the month of start_date and the 3 preceding months.

mozway
  • 194,879
  • 13
  • 39
  • 75

2 Answers2

2

You could do it with datetime however, getting the last day of a month is tricky.

pandas has nice time utility functions built on top of datetime:

import pandas as pd
from pandas.tseries.offsets import MonthEnd

start_date = "01-Feb-21"
N = 8

fmt = '%d-%b-%y'

# get month starts
s = pd.date_range(end=start_date, periods=N//2, freq='MS').to_series()
out = (pd.concat([s.dt.strftime(fmt),
                  (s + MonthEnd()).dt.strftime(fmt)],  # get month ends
                 axis=1)
         .iloc[::-1]                    # reverse order
         .to_numpy().ravel().tolist()   # convert to flat list
         # line above can also be replace with
         # .stack().to_list()
      )

output:

['01-Feb-21',
 '28-Feb-21',
 '01-Jan-21',
 '31-Jan-21',
 '01-Dec-20',
 '31-Dec-20',
 '01-Nov-20',
 '30-Nov-20']
mozway
  • 194,879
  • 13
  • 39
  • 75
0

This answer is a bit long but it uses only datetime and calendar module to get the expected result:

import calendar
from datetime import datetime, date


def generate_rolling_date(input_date, number_of_dates):
    """
    Generate Rolling Date
    :param input_date: Input Date (e.g. '01-Feb-21')
    :param number_of_dates: Number of rolling date to generate
    :return:
    """
    # Convert input_date to datetime according to the format
    parsed_date = datetime.strptime(input_date, "%d-%b-%y")

    # Separate Day, Month and Year
    parsed_day = parsed_date.day
    parsed_month = parsed_date.month
    parsed_year = parsed_date.year

    # Initialize an empty output list
    output = []

    # using calendar module, gets the total number of days i.e. last day in parsed_month
    _, num_days = calendar.monthrange(parsed_year, parsed_month)

    # Check if the parsed_day is the last day of the month or not, according to that set the start_date flag
    start_date = False if num_days == parsed_day else True

    # Loop through the number_of_dates to generate the rolling dates
    for i in range(number_of_dates):
        # If the parsed_month value is 0, then decrease the parsed_year by 1 and set the parsed_month value to 12
        if parsed_month == 0:
            parsed_year -= 1
            parsed_month = 12
        # Get the total number of days i.e. last day in parsed_month
        _, num_days = calendar.monthrange(parsed_year, parsed_month)
        # If start_date, then get the first day of the month and set start_date to False,
        # so we can get the last day of month in the next loop
        if start_date:
            # Get the first day of the month
            selected_day = date(parsed_year, parsed_month, 1)
            start_date = False
        else:
            # Get the last day of the month
            selected_day = date(parsed_year, parsed_month, num_days)
            # After getting the last day of the month, decrease the parsed_month by 1 to get the previous month
            parsed_month -= 1
            start_date = True
        # Append selected_day to the output
        output.append(date.strftime(selected_day, "%d-%b-%y"))
    # Return the output
    return output

print(generate_rolling_date("01-Feb-21", 8))

Which gives the following output:

['01-Feb-21',
 '28-Feb-21',
 '01-Jan-21',
 '31-Jan-21',
 '01-Dec-20',
 '31-Dec-20',
 '01-Nov-20',
 '30-Nov-20']
Tony Montana
  • 921
  • 18
  • 46