注册

上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}"


日历实现


假期判断


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


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


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


rili2.png


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


当然是有的,我们让别人给我们标记好了,自己等着用现成的,不就好了吗?那么哪里能找到这样的好心人呢?当然是有的,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=):
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-9.png


2024-10:
日历2024-10.png


2024-11:
日历2024-11.png


2024-12:
日历2024-12.png


引用与致谢


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


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


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


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


总结


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



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

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


作者:瞎老弟
来源:juejin.cn/post/7414013230954774579

0 个评论

要回复文章请先登录注册