以 Python 方式重用你的日历

昨天得到了一个 2016 年的日历。我脑海中浮现出一个有趣的问题:我什么时候可以重用这个日历,哪一年可以重用哪个旧日历?

2015 年 1 月 1 日是星期四。2016 年同一天是星期五。一旦你遵循此模式,你会很快认识到七年的基本周期被闰年打破。

很快发现,对于某些年份,需要几十年才能重用日历:2016 年是闰年,所以你不能在 2044 年重用它。

然而,有一个目前在 whencanireusethiscalendar.com 等在线服务中未实现的巧妙特性:你可以部分重用日历。

两个日历不匹配的原因通常是其中一个有闰日。然而,它们通常在闰日之前或从闰日后一天开始匹配。我们分别称这些年份为部分重用年份 (A) 和 (B)。一旦你考虑此选项(即你在闰日附近交换日历,假设我们有一年的 (A) 和 (B) 可重用日历),你就能重用日历 — 特别是像 2016 年这样的闰年日历。

基于此见解,我编写了一个仅使用核心库的简单暴力 Python 脚本。你可以简单地用要重用日历的年份在命令行调用它:

reuse-calendars.sh
python3 reuse-calendars.py 2015

该脚本使用试错方法,因为对于现代计算机来说搜索空间相当小:

reuse-calendars.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
计算你可以重用日历的年份列表。
此脚本计算三种类型的重用年份:
    - 完全重用年份,星期几每天都匹配
    - 部分重用年份 (A),到闰日(2 月 29 日)为止每天都匹配
    - 部分重用年份 (B),从闰日(2 月 29 日)后一天开始每天都匹配
部分重用年份按设计不包括完全重用年份。
"""
__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):
    """
    获取你可以重用日历的年份作为 ReuseYears 对象。
    """
    # 获取两个参考日期之一的 ISO 星期几编号
    # 参考日期 1 是一年的第一天
    # 参考日期 2 是闰日之后的任何日期
    refdate1 = lambda y: date(y, 1, 1).isoweekday()
    refdate2 = lambda y: date(y, 5, 1).isoweekday()
    fullYears = []
    partialYearsA = []  # 直到闰日
    partialYearsB = []  # 从闰日开始
    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='要获取的年份')
    args = parser.parse_args()

    print(getReuseYears(args.year))

Check out similar posts by category: Algorithms