用Python打造智能农历节日提醒系统:sxtwl库实战指南

每次春节前手忙脚乱查放假安排?总在节气当天才想起该吃饺子?传统节日对现代人来说越来越像彩蛋——不期而遇。其实用Python的sxtwl库加上30行代码,就能让这些特殊日期自动跳进你的日程表。下面我会带你从零构建一个会"算命"的智能提醒机器人,它不仅能告诉你下一个节气是哪天,还能提前三天发微信提醒你准备艾草浴。

1. 为什么需要农历智能提醒系统

公历日程管理工具遍地都是,但涉及到农历就集体失灵。我去年就曾因为记错农历生日,把家庭聚会订在了真实生日的前一周。更不用说那些固定在农历某日的传统节日——春节、端午、中秋,每年对应的公历日期都像在玩捉迷藏。

sxtwl这个冷门库恰好解决了这个问题。它不仅支持农历与公历的互转,还能直接查询节气、干支甚至星座。结合Python的自动化能力,我们可以实现:

  • 提前N天提醒重要农历节日(可自定义提醒间隔)
  • 节气当日早晨推送养生建议(比如"今日大暑,宜绿豆汤")
  • 自动生成年度农历节日日历(方便提前规划)
  • 企业微信/钉钉机器人通知(避免被邮件淹没)
# 安装核心库
pip install sxtwl schedule requests

提示:sxtwl最新版可能存在部分星座计算bug,建议固定安装2.0.6版本

2. 系统核心架构设计

这个提醒系统的关键在于三个模块的配合:

  1. 时间计算引擎:sxtwl负责所有农历相关计算
  2. 调度系统:schedule库实现每日自动检查
  3. 通知系统:根据场景选择邮件、企业微信或短信
graph TD
    A[每日定时触发] --> B{今天是节日/节气吗?}
    B -->|是| C[发送提醒]
    B -->|否| D[计算下一个节日]
    D --> E[设置临时提醒任务]

表:农历提醒系统状态转换逻辑

组件 功能说明 备选方案
sxtwl 农历日期计算与节气判断 zhdate(功能较少)
schedule 定时任务调度 APScheduler
企业微信机器人 通知推送 邮件/SMS/钉钉

3. 核心代码实现

先实现一个农历日期查询工具类,后续所有功能都基于这些基础方法:

import sxtwl

class LunarCalendar:
    def __init__(self):
        self.jqmc = ["冬至", "小寒", "大寒", "立春", "雨水", "惊蛰", 
                    "春分", "清明", "谷雨", "立夏", "小满", "芒种", 
                    "夏至", "小暑", "大暑", "立秋", "处暑", "白露", 
                    "秋分", "寒露", "霜降", "立冬", "小雪", "大雪"]
    
    def get_today_info(self):
        """获取今日农历信息"""
        day = sxtwl.fromSolar(2023, 8, 15)  # 实际使用应替换为datetime.now()
        info = {
            'lunar_date': f"{day.getLunarYear()}年{day.getLunarMonth()}月{day.getLunarDay()}日",
            'solar_date': f"{day.getSolarYear()}年{day.getSolarMonth()}月{day.getSolarDay()}日",
            'jieqi': self.jqmc[day.getJieQi()] if day.hasJieQi() else None
        }
        return info

测试这个基础功能:

calendar = LunarCalendar()
print(calendar.get_today_info())
# 输出示例: {'lunar_date': '2023年6月29日', 'solar_date': '2023年8月15日', 'jieqi': None}

接下来实现节日判断逻辑。我们需要先定义重要的传统节日:

class FestivalReminder:
    FESTIVALS = {
        "春节": {"month": 1, "day": 1, "advance_days": 7},
        "端午": {"month": 5, "day": 5, "advance_days": 3},
        "中秋": {"month": 8, "day": 15, "advance_days": 3},
        "冬至": {"is_jieqi": True, "advance_days": 1}
    }
    
    def check_festival(self, lunar_info):
        today = lunar_info['lunar_date']
        for name, config in self.FESTIVALS.items():
            if config.get('is_jieqi'):
                if lunar_info['jieqi'] == name:
                    return name
            else:
                if today.month == config['month'] and today.day == config['day']:
                    return name
        return None

4. 实现自动化提醒

现在把各个模块串联起来,实现完整的自动化流程:

import schedule
import time
from datetime import datetime

class AutoReminder:
    def __init__(self):
        self.calendar = LunarCalendar()
        self.reminder = FestivalReminder()
        
    def daily_check(self):
        today = datetime.now()
        lunar_info = self.calendar.get_today_info()
        festival = self.reminder.check_festival(lunar_info)
        
        if festival:
            self.send_notification(f"今天是{lunar_info['lunar_date']},{festival}快乐!")
        
    def send_notification(self, message):
        # 实际项目中替换为真实的通知发送逻辑
        print(f"[通知] {message}")
        
    def run(self):
        schedule.every().day.at("08:00").do(self.daily_check)
        while True:
            schedule.run_pending()
            time.sleep(60)

if __name__ == "__main__":
    bot = AutoReminder()
    bot.run()

对于需要提前多天提醒的场景,可以扩展check_festival方法:

def check_upcoming_festivals(self, days_ahead=7):
    upcoming = []
    for i in range(1, days_ahead + 1):
        future_date = datetime.now() + timedelta(days=i)
        lunar_info = self.calendar.get_solar_date(future_date.year, 
                                                 future_date.month, 
                                                 future_date.day)
        festival = self.check_festival(lunar_info)
        if festival:
            upcoming.append((future_date.strftime("%Y-%m-%d"), festival))
    return upcoming

5. 高级功能扩展

基础功能实现后,可以考虑这些增强功能:

  1. 多平台通知集成

    • 企业微信机器人Webhook
    • 钉钉自定义机器人
    • 短信网关(适合重要提醒)
  2. 智能提醒规则

    # 根据节日类型设置不同提醒规则
    REMINDER_RULES = {
        '春节': {'first_remind': 30, 'second_remind': 7, 'channel': 'sms'},
        '清明': {'remind': 3, 'channel': 'wechat'},
        '立夏': {'remind': 1, 'channel': 'email'}
    }
    
  3. 历史数据统计

    def get_year_festivals(self, year):
        """获取某年所有重要节日日期"""
        festivals = {}
        for month in range(1, 13):
            for day in range(1, 30):
                lunar_day = sxtwl.fromLunar(year, month, day)
                solar_date = f"{lunar_day.getSolarYear()}-{lunar_day.getSolarMonth()}-{lunar_day.getSolarDay()}"
                festival = self.check_festival({
                    'lunar_date': lunar_day,
                    'jieqi': self.jqmc[lunar_day.getJieQi()] if lunar_day.hasJieQi() else None
                })
                if festival:
                    festivals[solar_date] = festival
        return festivals
    
  4. 生日提醒特别处理

    def add_birthday_reminder(self, name, lunar_month, lunar_day):
        """添加农历生日提醒"""
        self.birthdays.append({
            'name': name,
            'month': lunar_month,
            'day': lunar_day
        })
    

6. 部署与优化建议

实际部署时,有几个实用技巧值得分享:

  1. 错误处理:sxtwl在处理极端日期时可能报错,需要捕获异常

    try:
        day = sxtwl.fromSolar(2200, 2, 30)  # 不存在的日期
    except Exception as e:
        print(f"日期转换错误: {e}")
    
  2. 性能优化:频繁创建sxtwl对象会影响性能,可以缓存常用日期

    from functools import lru_cache
    
    @lru_cache(maxsize=365)
    def get_day_info(year, month, day):
        return sxtwl.fromSolar(year, month, day)
    
  3. 时区处理:节气精确到分钟级别,需要考虑服务器时区

    import pytz
    from datetime import datetime
    
    tz = pytz.timezone('Asia/Shanghai')
    now = datetime.now(tz)
    
  4. 日志记录:所有提醒操作应该记录日志便于排查

    import logging
    logging.basicConfig(filename='reminder.log', level=logging.INFO)
    

这个项目最让我惊喜的是sxtwl对干支纪年的支持。有次我突发奇想,给系统加了个功能:每天推送晨间问候时附带当天的天干地支。结果这个"封建迷信"功能意外地受欢迎,团队成员都开始用"今天是癸卯年丙辰月"来替代普通的日期表达。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐