Compute the next salary payment date
Python · Python · advanced · greenfield
Adds the payroll scheduler's date helper. `next_pay_date` decides whether the anchor payment for the last-paid month is already behind (then rolls to the following month, handling the December wrap) and clamps the anchor day into whatever month it lands in via `calendar.monthrange`. Stepped an anchor-25 schedule through a full year and every month came out on the 25th.
Payroll service for ~200 employees; the returned date is when the salary batch is submitted to the bank.
Requirements
- Add `next_pay_date(last_pay, anchor_day)` for the payroll scheduler: given the date salaries were last paid and the company's payroll anchor day, return the payment date that FOLLOWS `last_pay`.
- Salaries are paid monthly on `anchor_day` (an integer 1–31). In months shorter than the anchor day, payment falls on the month's last day instead — e.g. anchor 30 pays Jan 30, Feb 28 (or 29 in leap years), Mar 30.
- The clamping is per-month only: the schedule must return to the true anchor day in every month long enough to hold it. A Feb 28 payment on an anchor-30 schedule is followed by Mar 30, never Mar 28.
- `last_pay` is always a date the schedule itself produced (the anchor day clamped into its month), as a `datetime.date`.
- Pure `datetime.date` arithmetic — no third-party libraries.
Files touched
- payroll/schedule.py
--- payroll/schedule.py +"""Payment-date arithmetic for the payroll scheduler.""" +import calendar +from datetime import date + + +def _days_in_month(year: int, month: int) -> int: + return calendar.monthrange(year, month)[1] + + +def next_pay_date(last_pay: date, anchor_day: int) -> date: + """The payment date following ``last_pay`` for a monthly schedule + anchored on ``anchor_day`` (1-31), clamped in short months. + """ + year, month = last_pay.year, last_pay.month + if last_pay.day >= anchor_day: + # This month's payment is already behind us - roll to the next month. + month += 1 + if month > 12: + month = 1 + year += 1 + return date(year, month, min(anchor_day, _days_in_month(year, month))) +