Full Payroll Script

import calendar from datetime import date,datetime, timedelta ''' takes a date input yyyy-mm-dd and returs the date string of the end of the month ''' def get_end_of_month_iso(iso_string: str) -> str: # Parse the incoming ISO string into a datetime object dt = datetime.fromisoformat(iso_string) # Get the last day number of the given month and year _, last_day = calendar.monthrange(dt.year, dt.month) # Replace the day component and preserve time/timezone metadata if present end_of_month_dt = dt.replace(day=last_day) # Return the result back as an ISO format string return end_of_month_dt.isoformat().split('T')[0] def is_weekend(iso_string: str) -> bool: # Parse the ISO 8601 string into a datetime object dt = datetime.fromisoformat(iso_string) # .weekday() returns 0 for Monday ... 5 for Saturday, 6 for Sunday return dt.weekday() >= 5 ''' takes two dates in yyyy-mm-dd format and returns all the dates between the two dates including the two inputted dates ''' def get_dates_between(start_iso: str, end_iso: str) -> list[str]: # Parse ISO strings into datetime objects start_dt = datetime.fromisoformat(start_iso).date() end_dt = datetime.fromisoformat(end_iso).date() # Calculate total days between start and end (inclusive) total_days = (end_dt - start_dt).days + 1 # Generate list of ISO formatted strings return [ (start_dt + timedelta(days=x)).isoformat() for x in range(total_days) ] ''' given an inputted date (in ISO date format: yyyy-mm-dd) if the inputted date is on a weekend, return the first weekday before ''' def get_nearest_past_weekday(iso_date_str: str) -> str: # Parse the ISO string into a datetime object dt = datetime.fromisoformat(iso_date_str) # .weekday() returns 0 for Monday ... 5 for Saturday, 6 for Sunday day_of_week = dt.weekday() if day_of_week == 5: # Saturday dt -= timedelta(days=1) # Move back to Friday elif day_of_week == 6: # Sunday dt -= timedelta(days=2) # Move back to Friday return dt.isoformat().split('T')[0] ''' given an inputted date (in ISO date format: yyyy-mm-dd) if the inputted date is on a weekend, return the first weekday after ''' def get_nearest_next_weekday(iso_date_str: str) -> str: # Parse the ISO string into a datetime object dt = datetime.fromisoformat(iso_date_str) # .weekday() returns 0 for Monday ... 5 for Saturday, 6 for Sunday day_of_week = dt.weekday() if day_of_week == 5: # Saturday dt += timedelta(days=2) # Move back to Friday elif day_of_week == 6: # Sunday dt += timedelta(days=1) # Move back to Friday return dt.isoformat().split('T')[0] ''' takes a set of paydates, and a list of employees and calculates a set of payments employees are give as an array of dictionaries such as employees = [ {'id':'1', 'start_date':'2026-01-01', 'salary': 100000}, {'id':'2', 'start_date':'2010-01-01', 'end_date':'2025-07-13', 'salary':70000}, {'id':'2', 'start_date':'2025-07-14', 'salary':90000} ] each employee needs an id, a start_date and a salary end_date is optional for each date in the list of dates, a cash flow is generated with the salary given on the employee record \ multiplied by the factor passed in the payment is adjusted proportionally if the employee start_date is between the current date and the last date, or if the employees end_date is between the current date and the next date ''' def payroll(dates, employees, factor=1.0): employee_map = {} for employee in employees: if employee['id'] not in employee_map: employee_map[employee['id']] = [] employee_map[employee['id']].append(employee) pass results = [] last_date = None for date in dates: for employee in employees: if employee['start_date']<=date: if 'end_date' not in employee or not employee['end_date'] employee['start_date'] : start_date = last_date if 'end_date' in employee and employee['end_date']< date:end_date = employee['end_date'] d1 = datetime.fromisoformat(start_date) d2 = datetime.fromisoformat(end_date) # Subtract the dates to get a timedelta object and extract .days day_difference1 = abs((d2 - d1).days) day_difference2 = abs((datetime.fromisoformat(date) - datetime.fromisoformat(last_date)).days) payment = employee['salary'] payment = payment * factor * (day_difference1/day_difference2) results.append({'date':date, 'id':employee['id'], 'payment':payment}) pass pass pass last_date = date pass return results

Example Script

import payroll as py ''' is a function that filters a set of dates for the pay dates. the pay dates specified by this function are the 15th and end of month. if the pay lands on a weekend, the first weekday before is chosen ''' def pay_dates(dates): results = [] for index,date in enumerate(dates): split = date.split('-') #if index == 0: results.append(date) if date == py.get_end_of_month_iso(date) :results.append(py.get_nearest_past_weekday(date)) elif split[2] == '15': results.append(py.get_nearest_past_weekday(date)) return results employees = [ {'id':'1', 'start_date':'2026-01-01', 'salary': 100000}, {'id':'2', 'start_date':'2010-01-01', 'end_date':'2025-07-13', 'salary':70000}, {'id':'2', 'start_date':'2025-07-14', 'salary':90000} ] paydays = [x for x in pay_dates(py.get_dates_between('2025-01-01', '2027-03-12')) if x is not None] #forecast the payroll cash flows cash_flows = py.payroll(paydays, employees, factor = 0.5/12) pass