在国内业务系统中,农历(阴历) 仍然被广泛使用,例如:
- 农历生日、纪念日
- 传统节日(春节、除夕、中秋)
- 日历 / 黄历 / 命理类应用
- 本地化系统展示
本文基于 lunar-python 农历算法库,使用 Flask 封装了一套 完整、可直接部署的农历转换 REST API,支持:
- ✅ 公历 → 农历
- ✅ 农历 → 公历(支持闰月)
- ✅ 当前农历日期查询
- ✅ 干支、生肖、节气、节日
- ✅ 完整中文描述
支持时间范围:1900 -- 2100 年
一、技术选型说明
1️⃣ lunar-python
选择 lunar-python 的原因:
- 农历算法成熟、权威
- API 设计清晰
- 支持干支、生肖、节气、节日
- 支持闰月(负数月份表示)
- 纯 Python,无外部依赖
bash
pip install lunar-python
2️⃣ Flask
- 轻量
- 易于部署
- 非常适合工具型 API / 内部服务
bash
pip install flask
二、API 设计说明
本示例实现了 4 个接口:
| 接口 | 说明 |
|---|---|
/api/to_lunar |
公历 → 农历 |
/api/to_solar |
农历 → 公历(支持闰月) |
/api/today_lunar |
获取当前农历 |
/ |
服务状态 & 接口说明 |
三、核心设计:统一农历数据结构
为了方便前端或其他系统使用,先对 lunar-python 的返回结果进行统一格式化。
农历信息格式化函数
python
def format_lunar(lunar):
"""统一格式化农历信息(完全适配 lunar_python 最新版)"""
month = lunar.getMonth() # 可能为负数(闰月)
is_leap_month = month < 0
abs_month = abs(month)
festivals = lunar.getFestivals() # 标准农历节日
other_festivals = lunar.getOtherFestivals() # 其他扩展节日
return {
"lunar_year": lunar.getYear(),
"lunar_month": abs_month,
"lunar_day": lunar.getDay(),
"is_leap_month": is_leap_month,
"chinese_string": lunar.toFullString(),
"full_info": lunar.toFullString(),
"ganzhi_year": lunar.getYearInChinese(),
"ganzhi_month": lunar.getMonthInChinese(),
"ganzhi_day": lunar.getDayInChinese(),
"shengxiao": lunar.getYearShengXiao(),
"jieqi": lunar.getJieQi() if lunar.getJieQi() else None,
"festivals": festivals if festivals else None,
"other_festivals": other_festivals if other_festivals else None
}
设计说明
-
闰月判断 :
month < 0 -
节日区分:
getFestivals():传统节日(春节、除夕)getOtherFestivals():扩展节日
-
输出结构稳定,方便前端直接使用
四、公历 → 农历 API
接口定义
GET /api/to_lunar
参数
| 参数 | 说明 |
|---|---|
| year | 公历年 |
| month | 公历月 |
| day | 公历日 |
代码实现
python
@app.route('/api/to_lunar', methods=['GET'])
def to_lunar():
try:
year = int(request.args.get('year'))
month = int(request.args.get('month'))
day = int(request.args.get('day'))
solar = Solar.fromYmd(year, month, day)
lunar = solar.getLunar()
return jsonify({
"success": True,
"solar_date": f"{year}-{month:02d}-{day:02d}",
"lunar": format_lunar(lunar)
})
except Exception as e:
return jsonify({
"success": False,
"error": str(e)
}), 400
示例请求
/api/to_lunar?year=2025&month=2&day=1
五、农历 → 公历 API(支持闰月)
接口定义
GET /api/to_solar
参数
| 参数 | 说明 |
|---|---|
| year | 农历年 |
| month | 农历月 |
| day | 农历日 |
| leap | 是否闰月(true / false) |
关键点:闰月处理
python
lunar_month = -month if leap else month
这是 lunar-python 的标准用法。
代码实现
python
@app.route('/api/to_solar', methods=['GET'])
def to_solar():
try:
year = int(request.args.get('year'))
month = int(request.args.get('month'))
day = int(request.args.get('day'))
leap = request.args.get('leap', 'false').lower() in ('true', '1', 'yes')
lunar_month = -month if leap else month
lunar = Lunar.fromYmd(year, lunar_month, day)
solar = lunar.getSolar()
return jsonify({
"success": True,
"lunar": format_lunar(lunar),
"solar_date": solar.toYmd()
})
except Exception as e:
return jsonify({
"success": False,
"error": str(e)
}), 400
六、获取当前农历日期 API
接口定义
GET /api/today_lunar
代码实现
python
@app.route('/api/today_lunar', methods=['GET'])
def today_lunar():
try:
now = datetime.now()
today_solar = Solar.fromDate(now)
today_lunar = today_solar.getLunar()
return jsonify({
"success": True,
"solar_date": today_solar.toYmd(),
"solar_datetime": today_solar.toYmdHms(),
"timestamp": now.isoformat(),
"lunar": format_lunar(today_lunar)
})
except Exception as e:
return jsonify({
"success": False,
"error": str(e)
}), 500
适合:
- 首页日历
- 今日农历展示
- 黄历 / 日签应用
七、服务启动与运行
python
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5006, debug=True)
启动后访问:
http://localhost:5006/
即可看到接口说明。
八、返回 JSON 示例(简化)
json
{
"lunar_year": 2024,
"lunar_month": 12,
"lunar_day": 4,
"is_leap_month": false,
"chinese_string": "甲辰年 腊月初四",
"shengxiao": "龙",
"festivals": ["春节"]
}
完整代码
bash
from flask import Flask, request, jsonify
from lunar_python import Solar, Lunar
from datetime import datetime
import traceback
app = Flask(__name__)
def format_lunar(lunar):
"""统一格式化农历信息(完全适配 lunar_python 最新版)"""
month = lunar.getMonth() # 可能为负数(闰月)
is_leap_month = month < 0
abs_month = abs(month)
festivals = lunar.getFestivals() # 标准农历节日(如春节、除夕)
other_festivals = lunar.getOtherFestivals() # 其他扩展节日
return {
"lunar_year": lunar.getYear(),
"lunar_month": abs_month,
"lunar_day": lunar.getDay(),
"is_leap_month": is_leap_month,
"chinese_string": lunar.toFullString(),
"full_info": lunar.toFullString(),
"ganzhi_year": lunar.getYearInChinese(),
"ganzhi_month": lunar.getMonthInChinese(),
"ganzhi_day": lunar.getDayInChinese(),
"shengxiao": lunar.getYearShengXiao(),
"jieqi": lunar.getJieQi() if lunar.getJieQi() else None,
"festivals": festivals if festivals else None, # 传统农历节日列表
"other_festivals": other_festivals if other_festivals else None # 其他节日
}
@app.route('/api/to_lunar', methods=['GET'])
def to_lunar():
try:
year = int(request.args.get('year'))
month = int(request.args.get('month'))
day = int(request.args.get('day'))
solar = Solar.fromYmd(year, month, day)
lunar = solar.getLunar()
return jsonify({
"success": True,
"solar_date": f"{year}-{month:02d}-{day:02d}",
"lunar": format_lunar(lunar)
})
except Exception as e:
app.logger.error(f"Error in to_lunar: {str(e)}\n{traceback.format_exc()}")
return jsonify({
"success": False,
"error": str(e),
"detail": str(e)
}), 400
@app.route('/api/to_solar', methods=['GET'])
def to_solar():
try:
year = int(request.args.get('year'))
month = int(request.args.get('month'))
day = int(request.args.get('day'))
leap = request.args.get('leap', 'false').lower() in ('true', '1', 'yes')
lunar_month = -month if leap else month
lunar = Lunar.fromYmd(year, lunar_month, day)
solar = lunar.getSolar()
return jsonify({
"success": True,
"lunar": format_lunar(lunar),
"solar_date": solar.toYmd()
})
except Exception as e:
app.logger.error(f"Error in to_solar: {str(e)}\n{traceback.format_exc()}")
return jsonify({
"success": False,
"error": str(e),
"detail": str(e)
}), 400
@app.route('/api/today_lunar', methods=['GET'])
def today_lunar():
try:
app.logger.info("Calling /api/today_lunar")
now = datetime.now()
today_solar = Solar.fromDate(now)
app.logger.info(f"Today solar: {today_solar.toYmd()} {today_solar.toYmdHms()}")
today_lunar = today_solar.getLunar()
return jsonify({
"success": True,
"solar_date": today_solar.toYmd(),
"solar_datetime": today_solar.toYmdHms(),
"timestamp": now.isoformat(),
"lunar": format_lunar(today_lunar)
})
except Exception as e:
error_msg = str(e)
tb = traceback.format_exc()
app.logger.error(f"Error in today_lunar: {error_msg}\n{tb}")
return jsonify({
"success": False,
"error": "Internal Server Error",
"detail": error_msg,
"traceback": tb
}), 500
@app.route('/', methods=['GET'])
def home():
return jsonify({
"message": "Lunar Calendar API is running! (lunar_python fully adapted)",
"current_time": datetime.now().isoformat(),
"endpoints": {
"to_lunar": "/api/to_lunar?year=2025&month=12&day=24",
"to_solar": "/api/to_solar?year=2025&month=11&day=5&leap=false",
"today_lunar": "/api/today_lunar"
}
})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5006, debug=True)
九、适用场景
- 📅 日历 / 黄历系统
- 🎂 农历生日管理
- 🧧 中国本土化应用
- 🌐 Web / 小程序后端
- 🧰 内部工具 API
十、总结
本文基于 lunar-python + Flask 实现了一套:
功能完整、结构清晰、可直接部署的农历转换 API
特点:
- 不依赖外部命令
- 支持闰月
- 返回结构友好
- 易于扩展
非常适合用于实际生产项目。