如何快速给数据库内生成工作日历?Python生成中国节假日工作表
本文最后更新于 2024年8月25日 上午
我们处理一些业务,比如:计算员工请假的时间工作日;就需要数据库内存在一张工作日历,记录调休和节假日。这个时候如何进行操作呢?
实际上,是有很多的公共接口。但是很多情况下,我们需要在内网环境下使用,这个时候就需要在数据库内生成工作日历表,如果使用频繁,甚至考虑缓存到中间件Redis内。
那么,如何在数据库内生成一个工作日历表呢?
离线日历库
如果只是简单的日历,那么其实系统自带的日历功能,也足够我们使用;比如iOS自带的日历,可以轻松滑动到300年后:
关键我们需要的是完整的放假表,例如: 2024年的9月14日,因为中秋节调休,周六要上班。所以,我们肯定需要一个工作日历的数据来源。
对于中国的节假日,最准确的肯定是中国政府网每年下半年发布次年的节假日和调休表(每次都是第一时间关注又要调休几次、最多要连续上几天的班╳╳○○),比如: 2024年的放假安排
如何获取一个离线的日历库呢?推荐以下几个项目:
- 中国节假日、调休日、工作日、24节气查询,农历阳历互转,支持 TS、CommonJS、UMD 模块化使用: https://github.com/vsme/chinese-days
- 判断一天是不是法定节假日/法定工作日(查看节假日安排): https://github.com/LKI/chinese-calendar
项目vsme/chinese-days
是有提供调休的JSON文件的:chinese-days.json,内部主要用两个部分:
- holidays: 放假的日期;
- workdays: 因节日而调休的日期。
理论上,你可以直接解析这个JSON文件,或者直接使用项目封装好的功能:
1 |
|
当然,为了可以生成SQL脚本,我们这里使用LKI/chinese-calendar
库,接下来我们就来演示。
演示网站
为了更会演示什么是中国节假日工作表;我利用本文的思路,搭建了一个在线演示网站:
比如,我们查询20240916,那么可以得到信息,这一天虽然是周一:
- 2024-09-16 是休息日ヾ(≧≦)〃;当天正在因为中秋而休息
- 目标日期的节气是: 白露,与此同时是该节气的第10天~
- 农历日期: 二零二四年八月十四日; 该日属于甲辰年癸酉月癸未日
网站使用的是腾讯云的轻量应用服务器进行搭建,另外也给大家申请到的福利:
另外,也可以考虑:
使用服务器的专属连接,享受超低折扣( ◔ ڼ ◔ )
支持创作
制作教程不易,如果热心的小伙伴,想支持创作,可以加入我们的电圈(还可以解锁远程协助、好友位😃):
- Mintimate的电圈: https://afdian.com/a/mintimate
- Mintimate的微信赞赏码 👉 如果认为本教程对你很有帮助,可以请我喝咖啡 ☕
当然,也欢迎在B站或YouTube上关注我们:
- Bilibili: https://space.bilibili.com/355567627
- YouTube: https://www.youtube.com/@mintimate/featured
更多:
节假日库选择
LKI/chinese-calendar
是基于Python的一个日期项目,如果你观察源码,你会发现日期数据是使用枚举类和Python字典存储的:
甚至结构和vsme/chinese-days
也是一样的;其实vsme/chinese-days
的JSON数据就是基于LKI/chinese-calendar
的。在vsme/chinese-days
的项目简介内,就有提及。
两个项目,都是基于MIT协议,在团队和企业内也可以放心使用。
比较有趣的是节日的中英名字映射、放假天数,LKI/chinese-calendar
使用的是枚举类:
1 |
|
倒不是使用枚举类有多么让人意想不到,只是发现原来放假的节日这么少😭。
至于这个库怎么使用,其实也很简单:
1 |
|
接下来,我们就封转一下。 使其生成SQL脚本。
数据库设计
既然需要一张表来存储工作日历,那么数据库的表应该如何设计?
我这里设计得比较简单,采用每天一条记录的方式进行记录,表名为WORK_CALENDAR
,主键为CALENDAR_DATE
:
YEAR | CALENDAR_DATE | DATE_TYPE | COMMENTS |
---|---|---|---|
2024 | 2024-01-01 | 1 | New Year’s Day |
2024 | 2024-01-02 | 0 | |
2024 | 2024-01-03 | 0 | |
2024 | 2024-01-04 | 0 | |
2024 | 2024-01-05 | 0 | |
2024 | 2024-01-06 | 3 | |
2024 | 2024-01-07 | 3 | |
解释一下各个字段:
-
YEAR
: 日期所属的年份; -
CALENDAR_DATE
: 数据对应的日期; -
DATE_TYPE
: 日期类型,0为普通工作日,1为节日放假,2为节日调休补班,3为周末放假; -
COMMENTS
: 备注节日。
可能你有更好的方法,可以评论区留言嗷。
代码实现
接下来,我们看看代码如何实现。
因为需要一次性生成一年的工作日历,所以我们需要先获取一年的数据日期,之后遍历数据日期,使用LKI/chinese-calendar
去解析每次的数据日期,将返回的结果包转为CSV或者拼接SQL。
流程图如下:
graph TD;
A[开始]:::start --> B{获取全年日期}:::process;
B -->|全年日期| C[循环遍历日期]:::loop;
C --> D{判断日期类型}:::decision;
D -->|节假日| E((生成SQL: 节假日)):::holiday;
D -->|周末| F((生成SQL: 周末)):::weekend;
D -->|工作日| G((生成SQL: 工作日)):::workday;
D -->|补班| H((生成SQL: 补班)):::compensation;
D -->|未匹配| I((打印: 未匹配)):::unmatched;
C --> J[收集数据]:::collect;
J --> K(写入SQL文件):::write-sql;
J --> L(导出CSV文件):::export-csv;
K --> Z[结束]:::endPoint;
L --> Z;
classDef start fill:#f9d635,stroke:#333,stroke-width:4px;
classDef process fill:#2196f3,stroke:#333,stroke-width:4px;
classDef loop fill:#4caf50,stroke:#333,stroke-width:4px;
classDef decision fill:#ff9800,stroke:#333,stroke-width:4px;
classDef holiday fill:#e91e63,stroke:#333,stroke-width:4px;
classDef weekend fill:#9c27b0,stroke:#333,stroke-width:4px;
classDef workday fill:#00bcd4,stroke:#333,stroke-width:4px;
classDef compensation fill:#ffeb3b,stroke:#333,stroke-width:4px;
classDef unmatched fill:#607d8b,stroke:#333,stroke-width:4px;
classDef collect fill:#8bc34a,stroke:#333,stroke-width:4px;
classDef write-sql fill:#795548,stroke:#333,stroke-width:4px;
classDef export-csv fill:#673ab7,stroke:#333,stroke-width:4px;
classDef endPoint fill:#f44336,stroke:#333,stroke-width:4px;
全年日期
我们先获取全年的日期,可以使用datetime
进行日期类型的创建:
1 |
|
之后,使用datetime.timedelta(days=1)
方法 ,获取一条的数值,并遍历累加即可。完整的代码为:
1 |
|
日期类型
在项目的代码内,我们知道LKI/chinese-calendar
返回的都是True
或Fasle
,比如:calendar.is_workday(date)
。
但是我们数据库内的设计,使用的是0~3
作为存储,并且还有一个注释字段,用来存储节日;所以,我们也需要设计一个枚举类,用来映射:
1 |
|
当我们进行日期的判断,就可以把布尔类型,经过枚举进行包转:
1 |
|
实现效果
最后,把上述的代码进行整理,核心判断内容就这样完成了:
1 |
|
再使用pandas进行包装一下,可以轻松得到SQL和CSV文件:
完整的代码,可以在我的GitHub仓库上找到:
END
哈哈,本次的分享就到这边。 其实是一件非常简单的事情,希望设计思路可以帮助到你。
如果你也需要工作日历,那么你可以直接clone代码并运行即可。
大家一般又是如何生成“工作日历”的呢?