上6休3上3休2……,为了理清这烧脑的调休安排我制作一个调休日历!

调休日历

前些天,使用python做了一个日历,可以打印出调休的节假日。效果还可以,但是遇到了一些小问题。

有一个朋友提出了这样一个问题:“日历确实是很好,但是,使用控制台print不怎么样,别人的日历都是很好看的,而我们的日历还只能用print。这太丢人了,我出门说自己是你的粉丝,都没什么面子了啊!”

还有一个朋友提出了另外一个问题:“在我们国家,‘农历’也是很重要的,比如说‘农历新年’是一年中最重要的日子。所以,我们的日历也应该能看到农历才比较好。”

好吧,这次就来解决一下这些问题!

农历

农历介绍

农历,也称为“阴历”,“月亮历”,“阴阳历”,是一种重要的历法,起源于中国的古代,这种历法同时结合了太阳运动,和月亮周期。其中月球围绕地球旋转的周期,大约为29.5天,这构成了农历的一个月,而一年通常是12个月。然而,一个太阳年(太阳公转一周)大概是365.24天,显然月亮的12个月不够这个数字,因此,为了填补到太阳年的时长,就会加入“闰月“,大概每3年就会出现一个”闰月“,”闰月“和公历中的“闰年”有异曲同工之妙。

那么农历有什么用呢?农历有很大的作用!节日安排,就是农历决定的(新年,中秋,端午等)。农业活动也和农历有很大的关系,比如说,仍然有很多农民,根据农历的指导,进行农业活动,什么时候播种,什么时候收割,都是农历决定的。还有很多人过生日,都会选择过农历生日。除此之外,像“季节变化”也和农历有微妙的关系,我们经常可以听到,“现在已经立秋了,已经是秋天了,马上就要凉快了。”,诸如此类的话,可见,农历在日常生活中发挥了重要作用。是我们祖先的伟大发明。

农历实现

那么,农历到底是怎么确定日子的呢?这和天文观测有很大的关系。我们要通过观察天象,来确定农历的准确性。比如说,在中国古代,就有专门的机构,如皇家天文台负责观测天文,然后调整历法,这是一个重要的活动。在历史上,历法也经过了多次修订。

到了现代,对于天文的观测,不再是必须的了。因为现代的科技较为发达,已经能够通过数学计算,历史天文数据推导等,精确的计算出农历。因此,即使我们没有“夜观星象”,也可以知道未来上百年的农历运作。

但是,我们既不会观察天象,也不会科学计算月亮运动,怎么办呢?当然没关系啦,因为,别人已经算好了,我们直接引用就可以了!

安装:pip install lunarcalendar

from lunarcalendar import Converter, Solar, Lunar


# 将公历日期转换为农历
solar = Solar(2024, 9, 17)
lunar = Converter.Solar2Lunar(solar)


# 输出结果为农历日期
print(lunar)


# 将农历日期转换为公历
lunar = Lunar(2024, 8, 15)
solar = Converter.Lunar2Solar(lunar)


# 输出结果为公历日期
print(solar)

转换为汉字日期

一般在农历中,我们并不使用阿拉伯数字的记录,而是常说,“正月初三”,“八月廿二”,这样的表达方式。因此,我们还需要将数字的农历,转为常见的汉字日期:

from lunarcalendar import Converter, Solar




def lunar_date_str(year, month, day):
    lunar = Converter.Solar2Lunar(Solar(year, month, day))
    leap_month = lunar.isleap
    months = ["正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "冬月", "腊月"]
    days = ["初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十",
            "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十",
            "廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十"]
    month_str = months[lunar.month - 1]
    if leap_month:
        month_str = "闰" + month_str
    day_str = days[lunar.day - 1]
    
    # 该实现是特别为了符合日历的实现,仅在每个月的第一天返回月份,例如正月初一,返回“正月”
    # 而其他日子,不返回月份,仅返回日期,例如正月初三,返回“初三”
    if lunar.day == 1:
        return month_str
    else:
        return day_str

如果要更广泛意义的月份加日期,只需要简单的修改返回值即可:f"{month_str}{day_str}"

日历实现

假期判断

因为“调休”的存在,所以我们的放假日期不总是固定的,每年都会有很大的变化,那么如何判断某个日子是不是节假日呢?是不是需要调休呢?

实际上,这是一件困难的事情,没有办法提前知道,只能等到每一年,国家公布了放假安排以后,我们才能够知道准确的放假调休日期。比如说,一些日历等不及,还没等公布放假安排,就已经开始提前印刷了,那么这样的日历,其上包含的信息,就是不完整的,他只能告诉你常规的节日和星期,没办法告诉你调休。

看过我上一期关于日历文章的,应该知道在当时,我是使用了“标记调休”的方式,实现这一点的,大概像这样:
在这里插入图片描述

这当然是简单有效,且可行的,只不过一次标记只能管一年,到了明年就不能用了,还得自己重新标记,况且,标记也是一件麻烦的事情,有没有什么更好的办法呢?

当然是有的,我们让别人给我们标记好了,自己等着用现成的,不就好了吗?那么哪里能找到这样的好心人呢?当然是有的,python有一个库叫做chinese-calendar,其中维护了每年的中国节假日,我们只需要使用这个库,让他告诉我们,今天休不休息,就好了。

安装:pip install chinese_calendar

import chinese_calendar as cc
from datetime import date


# 检查某一天是否是工作日或节假日
on_holiday, holiday_name = cc.get_holiday_detail(date(2024, 10, 1))


# 输出是否为假日,假日名称
print(on_holiday, holiday_name)

唉,世界上还是好人多啊!用上了这个,我们可以省很多事,真是好东西啊。

matplotlib绘制日历

matplotlib非常常用,今天就不主要介绍了,虽然它不常用于绘制日历,但是,它的功能其实是很广泛的,包括我们今天的绘制日历。

在下面的实现中,允许提供一个额外信息表,来覆盖默认的农历。也就是你可以通过extra_info_days来增加节日,纪念日的提示。

import datetime
import chinese_calendar as cc
import matplotlib.pyplot as plt


from calendar import monthrange
from lunarcalendar import Converter, Solar




class CalendarDrawer:
	plt.rcParams['font.family'] = 'SimHei' # 设置显示字体为黑体
    months = ["正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"]
    days = ["初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十",
            "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十",
            "廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十"]


    def __init__(self, year, month, extra_info_days=None):
        self.year = year
        self.month = month
        self.extra_info_days = extra_info_days or {}
        self.fig, self.ax = plt.subplots()


    def ax_init(self):
        self.ax.axis([0, 7, 0, 7])
        self.ax.axis("on")
        self.ax.grid(True)
        self.ax.set_xticks([])
        self.ax.set_yticks([])


    @staticmethod
    def lunar_date_str(year, month, day):
        lunar = Converter.Solar2Lunar(Solar(year, month, day))
        month_str = "闰" + CalendarDrawer.months[lunar.month - 1] if lunar.isleap else CalendarDrawer.months[lunar.month - 1]
        return month_str if lunar.day == 1 else CalendarDrawer.days[lunar.day - 1]


    def plot_month(self):
        self.ax.text(3.5, 7.5, f"{self.year}{self.month}月", color="black", ha="center", va="center")


    def plot_weekday_headers(self):
        for i, weekday in enumerate(["周一", "周二", "周三", "周四", "周五", "周六", "周日"]):
            x = i + 0.5
            self.ax.text(x, 6.5, weekday, ha="center", va="center", color="black")


    def plot_day(self, day, x, y, color):
        ex_day = datetime.date(self.year, self.month, day)
        day_info = f"{day}\n{self.extra_info_days.get(ex_day, self.lunar_date_str(self.year, self.month, day))}"
        self.ax.text(x, y, day_info, ha="center", va="center", color=color)


    def check_color_day(self, day):
        date = datetime.date(self.year, self.month, day)
        return "red" if cc.get_holiday_detail(date)[0] else "black"


    def save(self):
        self.ax_init()
        self.plot_month()
        self.plot_weekday_headers()


        weekday, num_days = monthrange(self.year, self.month)
        y = 5.5
        x = weekday + 0.5


        for day in range(1, num_days + 1):
            color = self.check_color_day(day)
            self.plot_day(day, x, y, color)
            weekday = (weekday + 1) % 7
            if weekday == 0:
                y -= 1
            x = weekday + 0.5


        plt.savefig(f"日历{self.year}-{self.month}.png")




if __name__ == "__main__":
    extra_info_days = {
        datetime.date(2024, 1, 1): "元旦",
        datetime.date(2024, 2, 10): "春节",
        datetime.date(2024, 2, 14): "情人节",
        datetime.date(2024, 2, 24): "元宵节",
        datetime.date(2024, 3, 8): "妇女节",
        datetime.date(2024, 4, 4): "清明节",
        datetime.date(2024, 5, 1): "劳动节",
        datetime.date(2024, 5, 12): "母亲节",
        datetime.date(2024, 5, 20): "520",
        datetime.date(2024, 6, 1): "儿童节",
        datetime.date(2024, 6, 10): "端午节",
        datetime.date(2024, 6, 16): "父亲节",
        datetime.date(2024, 8, 10): "七夕节",
        datetime.date(2024, 9, 17): "中秋节",
        datetime.date(2024, 10, 1): "国庆节",
        datetime.date(2024, 11, 1): "万圣节",
        datetime.date(2024, 11, 11): "双十一",
        datetime.date(2024, 12, 12): "双十二",
        datetime.date(2024, 12, 24): "平安夜",
        datetime.date(2024, 12, 25): "圣诞节"
    }


    calendar_drawer = CalendarDrawer(2024, 12, extra_info_days) # 第一个参数为年份,第二个参数为月份,第三个参数为额外信息字典
    calendar_drawer.save()

绘制结果,2024-09:
在这里插入图片描述

2024-10:
在这里插入图片描述

2024-11:

在这里插入图片描述

2024-12:

在这里插入图片描述

引用与致谢

日历样式,部分参考了便民查询:https://wannianrili.bmcx.com/

matplotlib绘制,部分参考了shimo164:https://medium.com/@shimo164/

详细的中国节假日,不再需要个人手动标记了,chinese-calendar:https://github.com/LKI/chinese-calendar

快速将公历转为农历,支持转换到2100年,LunarCalendar:https://github.com/wolfhong/LunarCalendar

总结

从我们的日历中,可以清晰的看出,中秋到国庆,我们经历了:

  1. 上6休3
  2. 上3休2
  3. 上5休1
  4. 上2休7
  5. 上5休1

嗯,确实是太烧脑了,没有日历,很难算的清楚啊,最后,那么问题来了,3+7到底等于几呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瞎老弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值