中国农历工具:从传统历法到现代应用的全栈实践指南
2026-04-22 10:26:13作者:尤峻淳Whitney
核心价值:解决传统历法数字化难题
在数字化时代,如何让应用程序理解"春节""清明"等传统节日?如何为用户提供精准的农历生日提醒?Chinese Calendar作为PHP生态中最完善的农历处理库,通过标准化API将复杂的传统历法计算封装为开发者友好的接口,解决了公历与农历转换、节气计算、干支纪年等核心需求,为传统文化数字化提供了可靠技术支撑。
场景化应用:从需求到实现的完整路径
构建电商平台节气营销系统
场景说明:某电商平台需要根据二十四节气自动推送时令商品(如立春茶叶、冬至汤圆),并在首页展示节气主题活动。
核心代码:
<?php
// 节气营销活动管理器
class SolarTermCampaignManager {
protected $calendar;
public function __construct() {
$this->calendar = new \Overtrue\ChineseCalendar\Calendar();
date_default_timezone_set('PRC'); // ⚠️ 必须设置中国时区
}
// 获取今日节气及对应活动
public function getTodayCampaign() {
$today = new DateTime();
$result = $this->calendar->solar(
(int)$today->format('Y'),
(int)$today->format('m'),
(int)$today->format('d')
);
// 若无节气则返回常规活动
if(empty($result['term'])) {
return $this->getRegularCampaign();
}
// 根据节气返回定制活动
$campaigns = [
'立春' => ['theme' => '迎春尝鲜', 'discount' => '85折'],
'清明' => ['theme' => '踏青季', 'discount' => '满200减50'],
'冬至' => ['theme' => '暖冬行动', 'discount' => '汤圆买一送一']
// 其他节气配置...
];
return $campaigns[$result['term']] ?? $this->getRegularCampaign();
}
private function getRegularCampaign() {
return ['theme' => '每日精选', 'discount' => '会员9折'];
}
}
// 应用示例
$manager = new SolarTermCampaignManager();
$todayCampaign = $manager->getTodayCampaign();
echo json_encode([
'today_term' => $result['term'] ?? '无',
'campaign' => $todayCampaign
]);
效果预览:
{
"today_term": "立春",
"campaign": {
"theme": "迎春尝鲜",
"discount": "85折"
}
}
开发会员农历生日提醒服务
场景说明:会员系统需要根据用户登记的农历生日,自动计算对应的公历日期并提前3天发送祝福短信。
核心代码:
<?php
class LunarBirthdayReminder {
protected $calendar;
protected $db;
public function __construct(PDO $db) {
$this->calendar = new \Overtrue\ChineseCalendar\Calendar();
$this->db = $db;
}
// 获取今日需要发送的生日提醒
public function getBirthdayReminders() {
$today = new DateTime();
$reminders = [];
// 获取所有用户农历生日
$stmt = $this->db->query("SELECT id, name, lunar_birthday FROM members");
while($user = $stmt->fetch(PDO::FETCH_ASSOC)) {
// 解析农历生日 (格式: Y-m-d 或 m-d)
list($lunarMonth, $lunarDay) = $this->parseLunarBirthday($user['lunar_birthday']);
// 计算今年对应的公历日期
$thisYear = (int)$today->format('Y');
$solarDate = $this->calendar->lunar($thisYear, $lunarMonth, $lunarDay);
// 计算提前3天的提醒日期
$reminderDate = (new DateTime("{$solarDate['gregorian_year']}-{$solarDate['gregorian_month']}-{$solarDate['gregorian_day']}"))
->modify('-3 days');
// 检查是否为今天需要提醒
if($reminderDate->format('Y-m-d') === $today->format('Y-m-d')) {
$reminders[] = [
'user_id' => $user['id'],
'name' => $user['name'],
'birthdate' => "{$solarDate['gregorian_year']}-{$solarDate['gregorian_month']}-{$solarDate['gregorian_day']}",
'message' => "亲爱的{$user['name']},您的生日将于3天后({$solarDate['lunar_month_chinese']}{$solarDate['lunar_day_chinese']})到来,我们为您准备了专属礼物!"
];
}
}
return $reminders;
}
private function parseLunarBirthday($birthdayStr) {
// 支持 "1990-05-05" 和 "05-05" 两种格式
$parts = explode('-', $birthdayStr);
if(count($parts) == 3) {
return [(int)$parts[1], (int)$parts[2]]; // 忽略年份,使用当前年份
}
return [(int)$parts[0], (int)$parts[1]];
}
}
// 使用示例
$db = new PDO('mysql:host=localhost;dbname=members', 'user', 'pass');
$reminder = new LunarBirthdayReminder($db);
$todayReminders = $reminder->getBirthdayReminders();
// 发送短信通知
foreach($todayReminders as $item) {
$this->sendSms($item['user_id'], $item['message']);
}
技术解析:农历计算的实现原理
数据字典:农历转换结果详解
| 字段名 | 类型 | 描述 | 示例 |
|---|---|---|---|
lunar_year |
int | 农历年份数字 | 2024 |
lunar_month |
int | 农历月份数字 | 1 |
lunar_day |
int | 农历日期数字 | 1 |
lunar_year_chinese |
string | 农历年份汉字 | 二零二四年 |
lunar_month_chinese |
string | 农历月份汉字 | 正月 |
lunar_day_chinese |
string | 农历日期汉字 | 初一 |
ganzhi_year |
string | 干支纪年 | 甲辰 |
ganzhi_month |
string | 干支纪月 | 丙寅 |
ganzhi_day |
string | 干支纪日 | 癸卯 |
animal |
string | 生肖 | 龙 |
term |
string | 节气名称 | 立春 |
is_leap |
bool | 是否闰月 | false |
constellation |
string | 星座 | 水瓶座 |
week_name |
string | 星期名称 | 星期日 |
核心算法解析
📅 农历计算核心原理:Chinese Calendar采用"农历数据表+算法修正"的混合实现方式,通过预定义的节气时间点和月相数据,结合天文计算修正,实现高精度的农历转换。
// 核心转换逻辑伪代码
public function solarToLunar($year, $month, $day) {
// 1. 计算从基准日期到目标日期的总天数
$totalDays = $this->countDaysFromBaseDate($year, $month, $day);
// 2. 查找对应的农历年份数据
$lunarYearData = $this->findLunarYearData($year);
// 3. 计算农历月和日
list($lunarMonth, $lunarDay, $isLeap) = $this->calculateLunarDate(
$lunarYearData, $totalDays
);
// 4. 计算干支信息
$ganzhi = $this->calculateGanzhi($year, $month, $day);
// 5. 确定节气信息
$term = $this->findSolarTerm($year, $month, $day);
return $this->formatResult(compact(
'lunarYear', 'lunarMonth', 'lunarDay',
'isLeap', 'ganzhi', 'term'
));
}
性能优化策略
通过缓存常用日期范围的转换结果,可显著提升系统性能。测试数据显示:
- 未缓存:单次转换平均耗时 0.8ms
- 已缓存:单次转换平均耗时 0.05ms(提升16倍)
推荐实现:
class CachedCalendar extends \Overtrue\ChineseCalendar\Calendar {
protected $cache;
protected $ttl = 86400; // 缓存24小时
public function __construct($cache) {
parent::__construct();
$this->cache = $cache;
}
public function solar($year, $month, $day, $hour = null) {
$key = "solar_{$year}_{$month}_{$day}_" . ($hour ?? '');
$cached = $this->cache->get($key);
if($cached !== null) {
return $cached;
}
$result = parent::solar($year, $month, $day, $hour);
$this->cache->set($key, $result, $this->ttl);
return $result;
}
// 同理实现lunar方法的缓存...
}
// 使用Redis缓存示例
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$calendar = new CachedCalendar($redis);
扩展实践:从基础集成到企业级应用
跨框架应用集成
Laravel框架集成
1. 创建服务提供者:
// app/Providers/ChineseCalendarServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Overtrue\ChineseCalendar\Calendar;
class ChineseCalendarServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('chinese-calendar', function ($app) {
$calendar = new Calendar();
date_default_timezone_set('PRC');
return $calendar;
});
}
}
2. 创建Facade:
// app/Facades/ChineseCalendar.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class ChineseCalendar extends Facade
{
protected static function getFacadeAccessor()
{
return 'chinese-calendar';
}
}
3. 配置与使用:
// config/app.php 添加服务提供者
'providers' => [
// ...
App\Providers\ChineseCalendarServiceProvider::class,
],
'aliases' => [
// ...
'ChineseCalendar' => App\Facades\ChineseCalendar::class,
],
// 控制器中使用
use App\Facades\ChineseCalendar;
public function showFestival()
{
$today = ChineseCalendar::solar(date('Y'), date('m'), date('d'));
return view('festival', compact('today'));
}
Symfony框架集成
1. 配置服务:
# config/services.yaml
services:
Overtrue\ChineseCalendar\Calendar:
calls:
- method: setTimezone
arguments: ['PRC']
App\Service\LunarService:
arguments:
$calendar: '@Overtrue\ChineseCalendar\Calendar'
2. 创建服务类:
// src/Service/LunarService.php
namespace App\Service;
use Overtrue\ChineseCalendar\Calendar;
class LunarService
{
private $calendar;
public function __construct(Calendar $calendar)
{
$this->calendar = $calendar;
}
public function getTodayLunarInfo()
{
return $this->calendar->solar(
(int)date('Y'),
(int)date('m'),
(int)date('d')
);
}
}
3. 在控制器中使用:
// src/Controller/FestivalController.php
namespace App\Controller;
use App\Service\LunarService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
class FestivalController extends AbstractController
{
public function index(LunarService $lunarService): Response
{
$todayInfo = $lunarService->getTodayLunarInfo();
return $this->render('festival/index.html.twig', [
'today_info' => $todayInfo,
]);
}
}
农历工具类封装模板
<?php
/**
* 企业级农历工具类封装
* 包含缓存、异常处理和常用功能封装
*/
class EnterpriseLunarTool {
const CACHE_PREFIX = 'lunar_';
const SUPPORT_START_YEAR = 1900;
const SUPPORT_END_YEAR = 2100;
private $calendar;
private $cache;
private $logger;
/**
* 构造函数
* @param object $cache 缓存实例,需实现get/set方法
* @param object $logger 日志实例,需实现info/error方法
*/
public function __construct($cache, $logger) {
$this->calendar = new \Overtrue\ChineseCalendar\Calendar();
$this->cache = $cache;
$this->logger = $logger;
date_default_timezone_set('PRC');
}
/**
* 公历转农历
* @param int $year 公历年
* @param int $month 公历月
* @param int $day 公历日
* @param int|null $hour 小时(可选)
* @return array|false 农历信息数组,失败返回false
*/
public function solarToLunar($year, $month, $day, $hour = null) {
try {
// 验证日期有效性
if(!$this->isValidSolarDate($year, $month, $day)) {
$this->logger->error("Invalid solar date: {$year}-{$month}-{$day}");
return false;
}
// 尝试从缓存获取
$cacheKey = self::CACHE_PREFIX . "solar_{$year}_{$month}_{$day}_" . ($hour ?? '');
$cachedData = $this->cache->get($cacheKey);
if($cachedData) {
return $cachedData;
}
// 调用转换方法
$result = $this->calendar->solar($year, $month, $day, $hour);
// 缓存结果
$this->cache->set($cacheKey, $result, 86400 * 30); // 缓存30天
return $result;
} catch (\Exception $e) {
$this->logger->error("Solar to lunar conversion failed: " . $e->getMessage());
return false;
}
}
/**
* 农历转公历
* @param int $year 农历年
* @param int $month 农历月
* @param int $day 农历日
* @param bool $isLeap 是否闰月
* @return array|false 公历信息数组,失败返回false
*/
public function lunarToSolar($year, $month, $day, $isLeap = false) {
try {
// 验证日期有效性
if(!$this->isValidLunarDate($year, $month, $day, $isLeap)) {
$this->logger->error("Invalid lunar date: {$year}-{$month}-{$day}" . ($isLeap ? '(leap)' : ''));
return false;
}
// 尝试从缓存获取
$cacheKey = self::CACHE_PREFIX . "lunar_{$year}_{$month}_{$day}_" . ($isLeap ? '1' : '0');
$cachedData = $this->cache->get($cacheKey);
if($cachedData) {
return $cachedData;
}
// 调用转换方法
$result = $this->calendar->lunar($year, $month, $day, $isLeap);
// 缓存结果
$this->cache->set($cacheKey, $result, 86400 * 30); // 缓存30天
return $result;
} catch (\Exception $e) {
$this->logger->error("Lunar to solar conversion failed: " . $e->getMessage());
return false;
}
}
/**
* 判断是否为传统节日
* @param array $lunarData 农历信息数组
* @return string|false 节日名称或false
*/
public function getTraditionalFestival($lunarData) {
$festivals = [
// 农历节日
'01-01' => '春节',
'01-15' => '元宵节',
'05-05' => '端午节',
'08-15' => '中秋节',
'09-09' => '重阳节',
// 公历节日(转换为农历检查)
// ...
];
$monthDay = sprintf("%02d-%02d", $lunarData['lunar_month'], $lunarData['lunar_day']);
return $festivals[$monthDay] ?? false;
}
/**
* 验证公历日期有效性
*/
private function isValidSolarDate($year, $month, $day) {
if($year < self::SUPPORT_START_YEAR || $year > self::SUPPORT_END_YEAR) {
return false;
}
return checkdate($month, $day, $year);
}
/**
* 验证农历日期有效性
*/
private function isValidLunarDate($year, $month, $day, $isLeap) {
if($year < self::SUPPORT_START_YEAR || $year > self::SUPPORT_END_YEAR) {
return false;
}
if($month < 1 || $month > 12) {
return false;
}
if($day < 1 || $day > 30) { // 农历最多30天
return false;
}
return true;
}
}
// 使用示例
// $cache = new SomeCacheImplementation();
// $logger = new SomeLoggerImplementation();
// $lunarTool = new EnterpriseLunarTool($cache, $logger);
// $lunarInfo = $lunarTool->solarToLunar(2024, 2, 10);
// if($festival = $lunarTool->getTraditionalFestival($lunarInfo)) {
// echo "今天是{$festival}";
// }
常见问题诊断
🔍 日期转换异常排查流程:
-
检查时区设置
echo date_default_timezone_get(); // 应输出"PRC"或"Asia/Shanghai" date_default_timezone_set('PRC'); // 确保设置正确时区 -
验证日期范围
if ($year < 1900 || $year > 2100) { throw new \InvalidArgumentException("日期超出支持范围(1900-2100)"); } -
闰月处理检查
// 农历转公历时需正确指定闰月参数 $result = $calendar->lunar(2020, 4, 1, true); // 第四个参数为true表示闰月 -
时辰计算规则
// 23点属于次日子时,需特别处理 $hour = 23; if ($hour >= 23) { $date = (new DateTime("{$year}-{$month}-{$day}"))->modify('+1 day'); $hour = 0; // 23点对应次日子时 }
通过以上系统化的实践指南,开发者可以快速掌握Chinese Calendar的核心功能,并将传统历法无缝集成到现代应用中,为用户提供更符合中国文化习惯的产品体验。无论是简单的日期转换还是复杂的节气营销系统,该工具都能提供可靠的技术支持,助力传统文化在数字时代的创新应用。
登录后查看全文
热门项目推荐
相关项目推荐
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust052
Kimi-K2.6Kimi K2.6 是一款开源的原生多模态智能体模型,在长程编码、编码驱动设计、主动自主执行以及群体任务编排等实用能力方面实现了显著提升。Python00- QQwen3.5-397B-A17BQwen3.5 实现了重大飞跃,整合了多模态学习、架构效率、强化学习规模以及全球可访问性等方面的突破性进展,旨在为开发者和企业赋予前所未有的能力与效率。Jinja00
MiniMax-M2.7MiniMax-M2.7 是我们首个深度参与自身进化过程的模型。M2.7 具备构建复杂智能体应用框架的能力,能够借助智能体团队、复杂技能以及动态工具搜索,完成高度精细的生产力任务。Python00
GLM-5.1GLM-5.1是智谱迄今最智能的旗舰模型,也是目前全球最强的开源模型。GLM-5.1大大提高了代码能力,在完成长程任务方面提升尤为显著。和此前分钟级交互的模型不同,它能够在一次任务中独立、持续工作超过8小时,期间自主规划、执行、自我进化,最终交付完整的工程级成果。Jinja00
ERNIE-ImageERNIE-Image 是由百度 ERNIE-Image 团队开发的开源文本到图像生成模型。它基于单流扩散 Transformer(DiT)构建,并配备了轻量级的提示增强器,可将用户的简短输入扩展为更丰富的结构化描述。凭借仅 80 亿的 DiT 参数,它在开源文本到图像模型中达到了最先进的性能。该模型的设计不仅追求强大的视觉质量,还注重实际生成场景中的可控性,在这些场景中,准确的内容呈现与美观同等重要。特别是,ERNIE-Image 在复杂指令遵循、文本渲染和结构化图像生成方面表现出色,使其非常适合商业海报、漫画、多格布局以及其他需要兼具视觉质量和精确控制的内容创作任务。它还支持广泛的视觉风格,包括写实摄影、设计导向图像以及更多风格化的美学输出。Jinja00
项目优选
收起
暂无描述
Dockerfile
683
4.38 K
Ascend Extension for PyTorch
Python
527
643
Claude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed.
Get Started
Rust
271
51
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
952
904
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
403
308
暂无简介
Dart
931
231
🎉 (RuoYi)官方仓库 基于SpringBoot,Spring Security,JWT,Vue3 & Vite、Element Plus 的前后端分离权限管理系统
Vue
1.58 K
913
华为昇腾面向大规模分布式训练的多模态大模型套件,支撑多模态生成、多模态理解。
Python
134
215
旨在打造算法先进、性能卓越、高效敏捷、安全可靠的密码套件,通过轻量级、可剪裁的软件技术架构满足各行业不同场景的多样化要求,让密码技术应用更简单,同时探索后量子等先进算法创新实践,构建密码前沿技术底座!
C
1.07 K
560
Oohos_react_native
React Native鸿蒙化仓库
C++
336
383