Matching the dates of a swap leg, bond, or loan schedules generated by different systems and libraries and explicitly specified in term sheets is usually straightforward, but sometimes hard.
For a swap, we typically see the accrual date, the first coupon date of each leg, and the maturity date. For example, we might see something like:
Termination / Maturity date: 15 January 2025
Leg 1
Effective / Accrual / Dated / Interest Commencement Date: 15 January 2021
First Swap Payment Date: 15 April 2021
Daycount basis: Quarterly, Actual / 360
Leg 2
Effective Date: January 15, 2021
First Swap Payment Date: July 15, 2021
Daycount basis: Semi-annually, 30 / 360
And for a bond / loan / note, we typically see something like:
Interest Payment Dates:
Semi-annually, each 15 January and 15 July in each year; the first such Interest payment Date being 15 July 2021 and the last such Interest Payment Date being 15 January 2025 / Maturity date.
but sometimes I've seen stranger bond schedules like:
Quarterly, each 15 January, 16 April, 15 July and 14 October in each year; the first such Interest payment Date being 15 July 2021 and the last such Interest Payment Date being 15 January 2025.
(OK, not quote this, but almost as weird). Or:
15 October, 15 November, 15 December and 15 January in each year; the first such Interest payment Date being 15 October 2021 and the last such Interest Payment Date being 15 January 2025.
(the 9-month float coupon reset from 9-month rate, while the 3 1-month coupons were reset from 1-month rates)
It's fairly common for the first and/or last coupon periods to be odd (short or long). Odd coupon periods in the middle are much less common.
As a general principle, it would seem like a good idea that whatever we see on the term sheet to a user interface and library.
Problem is, sometimes these things are a little ambiguous. E.g. are the days you see on the sheet already adjusted for weekends and holidays?
E.g. if the maturity is 16 August 2021 (Monday), could the dates be marching backwards from 15 August 2021 (Sunday) rather than from the adjusted maturity date?
For almost all instruments, the adjustment does not "stack". However a few instruments (all the examples I've seen are perpetual bonds), the adjstments "stack". For example, if a 3-month coupon starts accruing on 15 January and is paid not on 15 April, but on 16 April, because 15 April is a Sunday - then the next coupon in such a bond is not shorter by a day, but pays on July 16.
I've seen two possible approaches:
have a "roll day of the month" attribute of the schedule, similar to rolling on an end of the month. If you're marching from April 30 in any direcction, don't guess, but actually require the caller to specify whethey the roll rule is the 30th or month end. For example, a schedule starting on the 15th, rolling on the 30th (or on month end), and maturing again on the 15th, will have odd first and last periods, and might be on the 28th or 29th in February's.
If the instrument pays more than 1 coupon, then specify the (unadjusted) penultimate coupon date and march backwards from it. Or, if the instrument pays at least 1 coupon, then specify the (unadjusted) first coupon payment date, and march forward. (Whether you should always specify first and penultimate coupon dates, which are usually redundant, is a deep philosophical question.)
(Also allow to pass a flag for 'stack')
And I haven't even touched on Mexican, Russian, etc convention to pay coupons every 13 / 26 / 52 whole weeks instead of whole months.