Yesterday got a calendar for 2016. An interesting question came up my mind: When can I reuse this calendar, and for which year can I reuse which old calendar?
The 1st January 2015 was a Thursday. The same day in 2016 is a Friday. Once you follow this pattery you will quickly recognize that the base period of seven years is disrupted by leap years.
It quickly turns out that for some years it takes decades until you can reuse a calendar: 2016 is a leap yer, so you can not reuse it for 2044.
However, there’s a neat quirk that is currently unimplemented in online services like whencanireusethiscalendar.com: You can partially reuse a calendar.
The reason that two calendars don’t match is often a leap day in one of them. Quite often, however, they match either up to the leap day or starting from the day after the leap day. We call these years partial reuse years (A) and (B) respectively. Once you consider this option (i.e. you interchange the calendar somewhere around the leap day, assuming we have a both a (A) and a (B) reusable calendar for one year) you will be able to reuse calendars — especially leap-year calendars like 2016.
Based on this insight, I wrote a simple brute-force python script using only core libraries. You can simply call it on the command line with the year of the calendar you want to reuse:
python3 reuse-calendars.py 2015
The script uses a trial and error approach as the search space is rather small for a modern computer:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Compute a list of years where you can resue a calendar. This script computes three types of reuse years: - Full reuse years where every day of the week matches - Partial reuse years (A) where every day up to the leap day (Feb 29) matches - Partial reuse years (B) where every day starting from the day after he leap day (Feb 29) matches Partial reuse years do not include full reuse years by design. """ __copyright__ = "Copyright (c) 2015 Uli Köhler" __license__ = "Apache License v2.0" __version__ = "1.1" from datetime import date from collections import namedtuple ReuseYears = namedtuple('ReuseYears', ['year', 'fullReuseYears', 'partialReuseYearsA', 'partialReuseYearsB']) def getReuseYears(year, startYear=2005, stopYear=2050): """ Get years where you can reuse a calendar as ReuseYears object. """ # Get the ISO weekday number for one of two refdates # Refdate 1 is the first day of the year # Refdate 2 is any date AFTER a leap day refdate1 = lambda y: date(y, 1, 1).isoweekday() refdate2 = lambda y: date(y, 5, 1).isoweekday() fullYears =  partialYearsA =  # Until leap day partialYearsB =  # From leap day for otherYear in range(startYear, stopYear + 1): if otherYear == year: continue aMatch = refdate1(year) == refdate1(otherYear) bMatch = refdate2(year) == refdate2(otherYear) if aMatch and bMatch: fullYears.append(otherYear) elif aMatch: partialYearsA.append(otherYear) elif bMatch: partialYearsB.append(otherYear) return ReuseYears(year, fullYears, partialYearsA, partialYearsB) if __name__ == "__main__": import argparse parser = argparse.ArgumentParser() parser.add_argument('year', type=int, help='The year to get ') args = parser.parse_args() print(getReuseYears(args.year))