ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

MySQL农历公历对照表:两百年日历数据SQL脚本导入与查询

MySQL农历公历对照表:两百年日历数据SQL脚本导入与查询 简介面向需要处理公历与农历转换的MySQL开发者这份压缩包提供了1900—2100年完整的公历表和农历表数据及建表脚本适用于日历应用、事件管理、中国传统节日提醒等场景。资源仅包含1个SQL文件jp_lunar_solar.sql压缩后约1.94MB导入MySQL后即可获得两张基础日期表公历表记录年份、月份、日期、星期、节假日与描述农历表包含农历月份、农历日期、农历星期、农历节日及对应公历日期字段设计清晰便于按需查询某日的农历信息或根据农历节日反查公历日期尤其覆盖了闰月等复杂转换逻辑省去自行编写转换算法的时间。目前已有1887人学习下载适合为业务系统快速添加农历功能的初中级数据库开发者使用无论是万年历展示还是节气提醒都能直接基于这些表快速实现。1. 一份覆盖两百年的公历农历对照这份MySQL日历数据表解决的不只是日期换算做排班系统、日历提醒或者老系统改造的时候十有八九会被一个问题卡住农历到底怎么算。网上闰月算法一大堆但农历是阴阳合历置闰规则还跟着天文观测走用程序硬算翻车的概率远比你想象的大。这份mysql日历数据表直接把1900年到2100年的公历和农历对照数据做成了SQL脚本导入MySQL就能查春节、中秋、二十四节气都能直接落到具体日期上。适合正在做节日提醒、预约排期、报表统计按农历汇总的开发同学也适合不想在业务代码里维护一套农历算法的团队。2. 拆解jp_lunar_solar.sql公历表和农历表怎么把闰月、节气、节日塞进两百年2.1 公历表日期主键、星期与节假日标记解压压缩包后核心文件就是jp_lunar_solar.sql。按照最常见的组织方式脚本里会包含两张表一张存公历一张存农历。公历表的结构通常长这样CREATE TABLE solar_calendar ( id INT NOT NULL AUTO_INCREMENT, solar_date DATE NOT NULL, year SMALLINT NOT NULL, month TINYINT NOT NULL, day TINYINT NOT NULL, weekday TINYINT NOT NULL COMMENT 0周日,1周一,...,6周六, is_holiday TINYINT DEFAULT 0 COMMENT 1表示法定节假日或特殊日期, holiday_name VARCHAR(50) DEFAULT NULL COMMENT 节日名称如元旦、劳动节, PRIMARY KEY (id), UNIQUE KEY uk_solar_date (solar_date), KEY idx_year_month (year, month) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里的solar_date用的是 MySQL 原生的 DATE 类型而不是拆成三个 INT 字段。这么设计的好处是能用上日期函数比如BETWEEN、DATE_ADD()、YEAR()直接过滤省去自己拼接字符串的麻烦。id是自增主键实际查询中用得最多的是uk_solar_date这个唯一索引因为所有按日期查的操作都会走到它。weekday字段存的是星期数字注意注释里标明了取值规则不同脚本对 0 的定义可能不同有的 0 代表周日有的 0 代表周一这个坑后面会专门说。year、month、day三个字段看起来和solar_date冗余但实际很有用。比如你只想查某个月份的所有节假日走idx_year_month联合索引比在solar_date上做BETWEEN更高效索引范围更小。holiday_name存的是元旦、国庆这类法定节日的名称没有节日就存 NULL。这样业务侧查节假日时一条WHERE is_holiday 1就能全部捞出来不用再去维护一张单独的节日表。2.2 农历表闰月标记、对应的公历日期与节日字段农历表是整个数据包的重点因为农历的复杂性全在这张表上。常见结构如下CREATE TABLE lunar_calendar ( id INT NOT NULL AUTO_INCREMENT, solar_date DATE NOT NULL COMMENT 对应的公历日期, lunar_year SMALLINT NOT NULL COMMENT 农历年, lunar_month TINYINT NOT NULL COMMENT 农历月1-12, lunar_day TINYINT NOT NULL COMMENT 农历日1-30, is_leap_month TINYINT NOT NULL DEFAULT 0 COMMENT 1表示闰月, solar_term VARCHAR(30) DEFAULT NULL COMMENT 节气名如立春、夏至, lunar_festival VARCHAR(50) DEFAULT NULL COMMENT 农历节日如春节、中秋, PRIMARY KEY (id), UNIQUE KEY uk_solar_date (solar_date), KEY idx_lunar_ym (lunar_year, lunar_month, lunar_day) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这张表里最关键的是is_leap_month。农历十九年七闰每隔几年就会多出一个月比如常见的闰四月、闰六月。如果不加这个标记查询农历某月某日时会同时命中闰月和非闰月的数据结果翻倍。lunar_month只存 1 到 12一旦is_leap_month 1表示的才是真正的闰月。这个设计太重要了我见过不少人在查询时把闰月当成当月处理导致节日提醒全错位。solar_term存的是节气名二十四节气是按太阳在黄道上的位置划分的公历日期基本固定但和农历日期不对齐所以单独存一个字段最省事。lunar_festival存春节、端午、中秋这类节日和公历表的holiday_name是两套体系互不干扰。2.3 为什么用查表而不是算法换算农历转公历、公历转农历主流做法就两种算法计算和查表。算法计算需要内置一套几百年的数据表然后通过复杂的日期推算逻辑实时计算。查表的做法就是现在这样把每一天的公历农历对应关系提前算好存库里查询时直接匹配。我一般会推荐查表法。农历的精度依赖天文数据历史日期还好说未来的日期预测本身就是基于模型推算的自己在代码里维护一套推算公式遇到闰月就够喝一壶的。查表法的优势是快百万行级的数据量走唯一索引就是毫秒返回而且准确率有保证。代价是数据要定期更新不过覆盖到 2100 年说实话绝大多数业务系统活不到那个时候这份数据足够用了。3. 把SQL脚本导入MySQL三种导入方式与第一条验证查询3.1 命令行导入mysql重定向与source命令拿到jp_lunar_solar.sql后第一步是创建目标数据库并导入脚本。命令行是最直接的方式在 Linux 终端或者 Windows 的 cmd 下执行mysql -u root -p -e CREATE DATABASE IF NOT EXISTS calendar_db DEFAULT CHARACTER SET utf8mb4; mysql -u root -p --default-character-setutf8mb4 calendar_db jp_lunar_solar.sql第一条命令创建数据库显式指定utf8mb4字符集避免后续中文乱码。第二条命令把 SQL 文件重定向导入到calendar_db库中--default-character-setutf8mb4告诉 MySQL 客户端按 UTF-8 编码解析文件内容。这里有个小细节如果脚本里已经有CREATE DATABASE和USE语句第二条命令直接执行mysql -u root -p jp_lunar_solar.sql就行不需要先建库。我习惯先看一眼脚本开头的建库语句再决定。另一种方式是在已登录的 MySQL 会话里用source命令mysql -u root -pUSE calendar_db; SOURCE /path/to/jp_lunar_solar.sql;SOURCE是 MySQL 客户端的内部命令后面的路径是操作系统绝对路径。它的好处是能实时看到每一条 SQL 的执行结果哪一步报错马上就能定位。缺点是大脚本执行时终端输出会刷屏七万多行数据逐条 INSERT屏幕滚起来看起来有点吓人其实不用管等最后的Query OK提示就行。3.2 Navicat等图形化工具导入如果你用的是 Navicat、DBeaver 这类图形化客户端导入更直观。连接上数据库实例后在目标库上右键选择「运行 SQL 文件」选中jp_lunar_solar.sql执行即可。注意图形化工具一般会先解析整个文件耐心等它跑完。我习惯在导入前先确认连接本身的字符集设置。Navicat 连接属性里默认会按 UTF-8 发送 SQL但有些老版本默认是 GBK导入后中文变成问号重导前先改连接编码。另外Navicat 有「遇到错误继续」的选项默认是停住的如果脚本中途报错后面表数据就不全了。3.3 导入后的三行验证SQL导入完成别急着写业务代码先跑三条验证语句确认数据没问题SELECT COUNT(*) AS total_cnt FROM lunar_calendar; SELECT MIN(solar_date) AS min_date, MAX(solar_date) AS max_date FROM lunar_calendar; SELECT solar_date, lunar_year, lunar_month, lunar_day, is_leap_month FROM lunar_calendar WHERE solar_date 2024-02-10;200年的数据lunar_calendar的表行数应该在七万三千行左右。如果差得离谱要么导入中断要么脚本本身不完整。最小日期和最大日期分别对应公历 1900 年初和 2100 年末如果最小值明显晚于 1900 年说明表头数据缺失。最后一条查询是我最常用的抽查方式2024 年 2 月 10 日是甲辰年正月初一也就是春节查到这一行就说明数据可用性没问题。提示COUNT(*)在七万行的表上是秒回如果等了很久还没结果检查一下是不是走了全表扫描且没有主键。导入成功的表都带主键和索引正常不会出现这个情况。4. 避坑指南导入乱码、闰月漏查与日期边界的五个实测问题4.1 导入后中文全部变成问号现象查询holiday_name或solar_term字段返回的是???或者æ±æ¥这类乱码。原因SQL 文件本身是 UTF-8 编码但 MySQL 连接使用的字符集不是 UTF-8。Windows 下默认终端编码可能是 GBKMySQL 客户端在解析文件时按错误编码读取了中文字符。解决在导入前先设置连接字符集或者在命令行加参数。mysql -u root -p --default-character-setutf8mb4 calendar_db jp_lunar_solar.sql如果已经导入完了可以ALTER TABLE转一下字符集ALTER TABLE lunar_calendar CONVERT TO CHARACTER SET utf8mb4;这个操作会重写整张表七万行数据几十秒内能完成。从那以后我导任何带中文的 SQL第一时间先确认字符集这已经成了习惯。4.2 导入时提示 ERROR 1366 Incorrect string value现象执行SOURCE或mysql 导入时报ERROR 1366 (HY000): Incorrect string value: \xE5\x86\xAC... for column holiday_name。原因表的列定义里没有使用 UTF-8 字符集或者建表语句里写了DEFAULT CHARSETlatin1。如果脚本里建表语句没有显式指定字符集会继承数据库实例的默认设置而某些 MySQL 默认安装的字符集是 latin1。解决导入前检查库的默认字符集。SHOW CREATE TABLE lunar_calendar;确认表中字段的字符集如果有问题把整张表转成 utf8mb4 再重新导入。也可以在导入前先执行SET NAMES utf8mb4;告诉服务端当前连接按 UTF-8 处理文本。4.3 查询闰月日期时结果里混入了非闰月数据现象想查 2025 年闰六月的某一天用WHERE lunar_year 2025 AND lunar_month 6 AND lunar_day 15查返回了两条记录一条是六月十五一条是闰六月十五。原因忘了加is_leap_month条件。农历里闰月和平月的月份数字是重复的如果不区分查出来的就是两个月的并集。解决所有农历查询条件里都带上闰月标记。SELECT solar_date, lunar_year, lunar_month, lunar_day FROM lunar_calendar WHERE lunar_year 2025 AND lunar_month 6 AND lunar_day 15 AND is_leap_month 1;如果你要查的是普通六月就写is_leap_month 0。这个字段写进唯一索引的idx_lunar_ym后查询性能几乎没有影响。4.4 查询1900年1月初的农历日期查不到数据现象执行SELECT * FROM lunar_calendar WHERE solar_date BETWEEN 1900-01-01 AND 1900-01-31;返回结果只有后半个月的数据。原因农历 1900 年的春节对应公历 1900 年 1 月 31 日1900 年 1 月前半个月还属于农历己亥年腊月。如果数据从农历 1900 年正月初一作为起点那么这些日期在表里确实不存在。解决这是一个边界约束不是数据 bug。对接业务时要注意1900 年 1 月的头几天无法查出对应的农历日期如果有业务场景要覆盖这些时间需要自行向前补数据。同样2100 年 12 月 31 日已经是表数据的最后一行再往后没有数据。4.5 weekday字段和业务侧算出来的星期差一天现象前端拿到weekday字段展示星期几发现和日历 App 上显示的结果差了 1。原因weekday的取值约定不统一。有的脚本按 MySQL 的WEEKDAY()函数存0 代表周一6 代表周日有的按DAYOFWEEK()存1 代表周日7 代表周六。业务侧如果不看注释默认按 0周日 去算就会整体偏移一天。解决导入后先抽一天验证再在映射层明确取值规则。SELECT weekday, solar_date FROM solar_calendar WHERE solar_date 2024-02-10;这一天是周六如果查出来是 5说明 0周日 的约定如果查出来是 6说明 0周一 的约定。知道了规则在服务端做一次简单的转换再返回给前端就能对齐。5. 用日历表做节日提醒公农历互转SQL与业务表关联5.1 查询指定年份所有农历节日的公历日期把lunar_festival字段不为空的记录捞出来就能拿到一整年的农历节日清单SELECT solar_date, lunar_festival, lunar_year, lunar_month, lunar_day FROM lunar_calendar WHERE lunar_year 2024 AND is_leap_month 0 AND lunar_festival IS NOT NULL ORDER BY solar_date;is_leap_month 0在这里是必须的否则同一个月份里的闰月和平月会各返回一条同名节日记录。ORDER BY solar_date让结果按公历日期排方便直接对接日历控件。这套 SQL 做节日订阅功能很顺手前端拿到结果后直接把solar_date标记到日历上不需要在代码里逐条算农历。5.2 封装一个公历转农历的MySQL函数业务查询中经常需要按公历日期实时转农历每次拼 SQL 太啰嗦可以封装成函数DELIMITER // CREATE FUNCTION solar_to_lunar(input_date DATE) RETURNS VARCHAR(50) READS SQL DATA DETERMINISTIC BEGIN DECLARE result VARCHAR(50); DECLARE leap_flag VARCHAR(4); SELECT CONCAT(lunar_year, 年, IF(is_leap_month 1, 闰, ), lunar_month, 月, lunar_day, 日) INTO result FROM lunar_calendar WHERE solar_date input_date; RETURN result; END // DELIMITER ;函数接收一个 DATE 参数查表返回格式化后的农历字符串。IF(is_leap_month 1, 闰, )会在闰月前自动加一个「闰」字。READS SQL DATA和DETERMINISTIC是函数声明前者告诉 MySQL 这个函数要读数据后者表示相同输入一定得到相同输出。调用方式很简单SELECT solar_to_lunar(2024-02-10);返回结果是2024年正月1日。注意函数的缺省行为如果查不到数据比如传入 1900 年 1 月 1 日INTO不会赋值函数返回 NULL。如果你的业务不允许空值可以在声明一个默认值或者在外面用IFNULL()包一下。5.3 业务表按农历关联日程与节日的提醒场景日历表最大的价值是和业务表做关联。比如你的用户表里存了生日用户要的是农历生日提醒直接关联农历表就能筛出来SELECT u.id, u.name, u.birthday, l.solar_date AS this_year_lunar_birthday FROM user_info u JOIN lunar_calendar l ON l.lunar_month MONTH(u.birthday_lunar_month) AND l.lunar_day DAY(u.birthday_lunar_day) AND l.lunar_year 2025 AND l.is_leap_month 0 ORDER BY l.solar_date;这里lunar_year 2025指定了要查的农历年份lunar_month和lunar_day用用户的农历生日字段匹配。一个典型场景是用户表里冗余了birthday_lunar_month和birthday_lunar_day两个整数列当年农历生日对应的公历日期直接查出来做提醒。如果用户的生日落在闰月这里就要注意正常年份is_leap_month 0过滤出来的就是非闰月对应日期。注意农历生日有个特有的规则——如果用户出生在闰月通常按非闰月过生日。这条业务逻辑要在代码层做判断数据库里只存数据即可。6. 数据完整的三个验证技巧上线前把这张表当权威数据源之前数据表拿过来直接用是最省事的但把它当成权威数据源之前我习惯做一轮针对性验证这里分享三个自己常用的技巧。第一个技巧是抽查已知农历日期。每年春节的日期是可以提前确认的拿最近两三年的春节日核对。2024 年春节是 2 月 10 日2023 年春节是 1 月 22 日用这两条去查solar_date确认得到的lunar_month 1且lunar_day 1。这一步能快速排除导入不完整和行偏移的问题。第二个技巧是检查闰月分布是否稀疏且合理。农历十九年七闰200 年里闰月年份大约 70 个。用聚合查询把闰月的情况拉出来SELECT lunar_year, lunar_month, COUNT(*) AS days_cnt FROM lunar_calendar WHERE is_leap_month 1 GROUP BY lunar_year, lunar_month ORDER BY lunar_year;结果里每行代表一个闰月天数在 29 或 30 左右。如果某一年出现了两个闰月记录或者相邻两年连续闰月这里就有问题。正常闰月不会连续出现也不会一年闰两次。第三个技巧是检查尾行数据。把最后一条记录拉出来确认是 2100 年 12 月 31 日并且对应的农历日期在腊月。1900 年到 2100 年的边界是这份数据包的承诺边界错了中间数据再准也不敢用。从那以后我每次导入这份日历表都会强制把这三条验证 SQL 跑一遍再交付给业务方省得后续排查数据问题时背锅。如果验证全过这张表就可以放心地接进系统里把公农历转换、节日提醒这些活全部交给数据库希望帮到你。本文还有配套的精品资源点击获取
返回列表