Python实战3分钟实现公历农历双向转换工具农历日期在我们的日常生活中扮演着重要角色——从春节团聚到中秋赏月从生辰八字到传统节气。但每次需要查询农历时大多数人还是依赖手动翻阅日历或搜索网页。对于开发者、数据分析师或是需要批量处理日期数据的专业人士来说这种低效方式显然不够优雅。本文将带你用Python快速构建一个公历农历互查工具并深入解析背后的数据原理。1. 农历数据的奥秘十六进制编码解析农历与公历的最大区别在于其复杂的计算规则。农历一个月约29.53天一年约354天比公历少11天左右。为了协调这种差异农历采用十九年七闰的方法即在19年中加入7个闰月。原始数据中的十六进制数字如0x04bd8实际上是一个24位的二进制编码其结构如下# 24位农历数据分解示意 lunar_data 0x04bd8 # 示例数据 binary_str bin(lunar_data)[2:].zfill(24) # 转换为24位二进制 leap_month_size binary_str[:4] # 前4位闰月大小 month_sizes binary_str[4:16] # 中间12位每月大小 leap_month binary_str[16:] # 后4位闰月月份这个编码结构可以表示为位段长度含义示例值前4位4闰月大小1大月30天0小月29天0100中间12位12每月大小1大月0小月101111011000后4位4闰月月份0无闰月1000实际应用示例对于1900年的数据0x04bd8前4位0100表示闰月是大月30天中间12位101111011000表示1-12月的大小月分布后4位10008表示第8个月后有一个闰月2. Python实现核心转换逻辑理解了数据编码后我们可以开始构建转换工具。首先需要准备基础数据# 1900-2100年的农历数据 lunar_info [ 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, # 1900-1909 # ...完整数据应包含所有年份 ] # 春节日期数据 spring_festival [ 0x76c011f, 0x76d0213, 0x76e0208, 0x76f011d, 0x7700210, 0x7710204, 0x7720119, 0x773020d, 0x7740204, 0x7750116, # 1900-1909 # ...完整数据应包含所有年份 ]2.1 公历转农历函数实现def solar_to_lunar(solar_date): 将公历日期转换为农历日期 year solar_date.year sf_data spring_festival[year - 1900] # 解析春节日期 sf_year (sf_data 16) 0xFFFF sf_month (sf_data 8) 0xFF sf_day sf_data 0xFF spring_date date(sf_year, sf_month, sf_day) # 计算距离春节的天数 delta_days (solar_date - spring_date).days if delta_days 0: # 处理跨年情况 year - 1 sf_data spring_festival[year - 1900] spring_date date((sf_data 16) 0xFFFF, (sf_data 8) 0xFF, sf_data 0xFF) delta_days (solar_date - spring_date).days # 解析农历年数据 lunar_data lunar_info[year - 1900] leap_month lunar_data 0xF leap_month_days 29 ((lunar_data 16) 0x1) if leap_month else 0 # 计算农历月份和日期 month 1 day delta_days 1 for m in range(12): month_days 29 ((lunar_data (11 - m)) 0x1) if day month_days: day - month_days month 1 else: break # 处理闰月 if m 1 leap_month: if day leap_month_days: day - leap_month_days else: return year, month, day, True return year, month, day, False2.2 农历转公历函数实现def lunar_to_solar(lunar_year, lunar_month, lunar_day, is_leapFalse): 将农历日期转换为公历日期 # 获取春节日期 sf_data spring_festival[lunar_year - 1900] spring_date date((sf_data 16) 0xFFFF, (sf_data 8) 0xFF, sf_data 0xFF) # 解析农历年数据 lunar_data lunar_info[lunar_year - 1900] leap_month lunar_data 0xF # 计算总天数 total_days 0 for m in range(lunar_month - 1): if m 1 leap_month and is_leap: total_days 29 ((lunar_data 16) 0x1) else: total_days 29 ((lunar_data (11 - m)) 0x1) total_days lunar_day - 1 return spring_date timedelta(daystotal_days)3. 封装为实用工具类为了让代码更易用我们可以将其封装为一个完整的工具类class LunarConverter: def __init__(self): self.lunar_info [...] # 完整的农历数据 self.spring_festival [...] # 完整的春节数据 def solar_to_lunar(self, solar_date): # 实现同上 pass def lunar_to_solar(self, lunar_year, lunar_month, lunar_day, is_leapFalse): # 实现同上 pass def get_lunar_year_name(self, year): 获取农历年名称甲子年等 celestial [甲, 乙, 丙, 丁, 戊, 己, 庚, 辛, 壬, 癸] terrestrial [子, 丑, 寅, 卯, 辰, 巳, 午, 未, 申, 酉, 戌, 亥] return celestial[(year - 4) % 10] terrestrial[(year - 4) % 12] 年 def get_lunar_month_name(self, month, is_leap): 获取农历月份名称 months [正月, 二月, 三月, 四月, 五月, 六月, 七月, 八月, 九月, 十月, 冬月, 腊月] return (闰 if is_leap else ) months[month - 1] def get_lunar_day_name(self, day): 获取农历日名称 days [初一, 初二, 初三, 初四, 初五, 初六, 初七, 初八, 初九, 初十, 十一, 十二, 十三, 十四, 十五, 十六, 十七, 十八, 十九, 二十, 廿一, 廿二, 廿三, 廿四, 廿五, 廿六, 廿七, 廿八, 廿九, 三十] return days[day - 1]4. 实战应用与扩展4.1 批量处理日期数据假设我们有一组用户生日数据需要转换为农历converter LunarConverter() user_birthdays [ 1990-05-15, 1985-08-23, 1995-11-07, 2000-01-01, 2010-03-15 ] for bday in user_birthdays: solar_date datetime.strptime(bday, %Y-%m-%d).date() ly, lm, ld, is_leap converter.solar_to_lunar(solar_date) print(f{bday} → {converter.get_lunar_year_name(ly)} f{converter.get_lunar_month_name(lm, is_leap)} f{converter.get_lunar_day_name(ld)})4.2 节气计算扩展农历系统中节气也是重要组成部分。我们可以扩展工具类来支持节气计算# 节气数据以1900年为例 solar_terms { 1900: [ 1900-01-06, 1900-01-20, 1900-02-04, 1900-02-19, 1900-03-06, 1900-03-21, 1900-04-05, 1900-04-20, 1900-05-06, 1900-05-21, 1900-06-06, 1900-06-21, 1900-07-07, 1900-07-23, 1900-08-08, 1900-08-23, 1900-09-08, 1900-09-23, 1900-10-08, 1900-10-23, 1900-11-07, 1900-11-22, 1900-12-07, 1900-12-22 ], # 其他年份数据... } class LunarConverter: # ...之前的方法... def get_solar_term(self, date): 获取指定日期的节气信息 year date.year terms solar_terms.get(year, []) for term in terms: term_date datetime.strptime(term, %Y-%m-%d).date() if date term_date: return term_names[terms.index(term) % 24] return None4.3 命令行工具实现最后我们可以将工具封装为命令行应用import argparse def main(): parser argparse.ArgumentParser(description公历农历转换工具) parser.add_argument(-s, --solar, help公历日期 (YYYY-MM-DD)) parser.add_argument(-l, --lunar, nargs3, help农历日期 年 月 日 (可加--leap标记闰月)) parser.add_argument(--leap, actionstore_true, help标记当前农历月份为闰月) args parser.parse_args() converter LunarConverter() if args.solar: date_obj datetime.strptime(args.solar, %Y-%m-%d).date() ly, lm, ld, is_leap converter.solar_to_lunar(date_obj) print(f公历 {args.solar} 对应的农历日期为) print(f{converter.get_lunar_year_name(ly)} f{converter.get_lunar_month_name(lm, is_leap)} f{converter.get_lunar_day_name(ld)}) if args.lunar: year, month, day map(int, args.lunar) solar_date converter.lunar_to_solar(year, month, day, args.leap) print(f农历 {year}年{闰 if args.leap else }{month}月{day}日 对应的公历日期为) print(solar_date.strftime(%Y-%m-%d)) if __name__ __main__: main()使用时只需在命令行执行python lunar_converter.py -s 2023-05-15 # 或 python lunar_converter.py -l 2023 4 1这个工具不仅解决了公历农历互查的问题其模块化设计还便于集成到其他系统中。无论是批量处理用户数据还是构建更复杂的日历应用这套核心逻辑都能提供可靠支持。