本文目录导读:

在PHP中实现公历与农历的互转,有几种常见的方法:
方法1:使用第三方库(推荐)
安装 overtrue/lunar 库
composer require overtrue/lunar
公历转农历
<?php
require 'vendor/autoload.php';
use Overtrue\Lunar\Lunar;
// 公历转农历
$lunar = Lunar::fromDate('2024-01-15');
echo $lunar->format('Y-m-d'); // 输出:2023-十二月初五
// 或者使用对象方式
$lunar = Lunar::make(2024, 1, 15);
echo $lunar->getYear() . '年'; // 2023年
echo $lunar->getMonth() . '月'; // 十二月
echo $lunar->getDay() . '日'; // 初五
农历转公历
<?php
require 'vendor/autoload.php';
use Overtrue\Lunar\Lunar;
// 农历转公历
$lunar = Lunar::make(2023, 12, 5);
$solar = $lunar->toSolar();
echo $solar->format('Y-m-d'); // 输出:2024-01-15
方法2:自定义函数实现
这是一个完整的自定义实现:
<?php
class LunarCalendar {
// 1900-2100年农历数据
private static $lunarInfo = [
0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,
0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,
0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,0x1ab54,0x02b60,0x09570,0x052f2,0x04970,
0x06566,0x0d4a0,0x0ea50,0x06e95,0x05ad0,0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,
0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,
0x06ca0,0x0b550,0x15355,0x04da0,0x0a5b0,0x14573,0x052b0,0x0a9a8,0x0e950,0x06aa0,
0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,0x05260,0x0f263,0x0d950,0x05b57,0x056a0,
0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,0x0d250,0x0d558,0x0b540,0x0b6a0,0x195a6,
0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,
0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,0x06b58,0x05ac0,0x0ab60,0x096d5,0x092e0,
0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,
0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,
0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,
0x05aa0,0x076a3,0x096d0,0x04afb,0x04ad0,0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,
0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0,
0x14b63,0x09370,0x049f8,0x04970,0x064b0,0x168a6,0x0ea50,0x06aa0,0x1a6c4,0x0aae0,
0x092e0,0x0d2e3,0x0c960,0x0d557,0x0d4a0,0x0da50,0x05d55,0x056a0,0x0a6d0,0x055d4,
0x052d0,0x0a9b8,0x0a950,0x0b4a0,0x0b6a6,0x0ad50,0x055a0,0x0aba4,0x0a5b0,0x052b0,
0x0b273,0x06930,0x07337,0x06aa0,0x0ad50,0x14b55,0x04b60,0x0a570,0x054e4,0x0d160,
0x0e968,0x0d520,0x0daa0,0x16aa6,0x056d0,0x04ae0,0x0a9d4,0x0a4d0,0x0d150,0x0f252,
0x0d520
];
// 天干
private static $tianGan = ['甲','乙','丙','丁','戊','己','庚','辛','壬','癸'];
// 地支
private static $diZhi = ['子','丑','寅','卯','辰','巳','午','未','申','酉','戌','亥'];
// 生肖
private static $shengXiao = ['鼠','牛','虎','兔','龙','蛇','马','羊','猴','鸡','狗','猪'];
// 农历月份
private static $lunarMonth = ['正','二','三','四','五','六','七','八','九','十','冬','腊'];
// 农历日期
private static $lunarDay = [
'初一','初二','初三','初四','初五','初六','初七','初八','初九','初十',
'十一','十二','十三','十四','十五','十六','十七','十八','十九','二十',
'廿一','廿二','廿三','廿四','廿五','廿六','廿七','廿八','廿九','三十'
];
/**
* 公历转农历
*/
public static function solarToLunar($year, $month, $day) {
// 基准日期:1900年1月31日(农历庚子年正月初一)
$baseDate = new DateTime('1900-01-31');
$targetDate = new DateTime("$year-$month-$day");
$offset = $baseDate->diff($targetDate)->days;
$lunarYear = 1900;
$lunarMonth = 1;
$lunarDay = 1;
$isLeap = false;
// 计算农历年
$i = 1900;
while ($offset > 0) {
$yearDays = self::lunarYearDays($i);
if ($offset < $yearDays) {
break;
}
$offset -= $yearDays;
$i++;
}
$lunarYear = $i;
// 计算农历月
$leapMonth = self::leapMonth($lunarYear);
$isLeap = false;
for ($j = 1; $j <= 12; $j++) {
$monthDays = self::lunarMonthDays($lunarYear, $j);
if ($offset < $monthDays) {
break;
}
$offset -= $monthDays;
if ($leapMonth > 0 && $j == $leapMonth) {
$leapDays = self::leapMonthDays($lunarYear);
if ($offset < $leapDays) {
$isLeap = true;
break;
}
$offset -= $leapDays;
$j--;
}
}
$lunarMonth = $j;
$lunarDay = $offset + 1;
// 获取天干地支年
$ganIndex = ($lunarYear - 4) % 10;
$zhiIndex = ($lunarYear - 4) % 12;
return [
'year' => $lunarYear,
'month' => $lunarMonth,
'day' => $lunarDay,
'isLeap' => $isLeap,
'yearName' => self::$tianGan[$ganIndex] . self::$diZhi[$zhiIndex] . '年',
'shengXiao' => self::$shengXiao[$zhiIndex] . '年',
'monthName' => ($isLeap ? '闰' : '') . self::$lunarMonth[$lunarMonth - 1] . '月',
'dayName' => self::$lunarDay[$lunarDay - 1]
];
}
/**
* 农历转公历
*/
public static function lunarToSolar($lunarYear, $lunarMonth, $lunarDay, $isLeap = false) {
// 计算农历年的总天数
$yearDays = self::lunarYearDays($lunarYear);
// 计算到农历年初的天数
$days = 0;
$leapMonth = self::leapMonth($lunarYear);
for ($i = 1; $i < $lunarMonth; $i++) {
$days += self::lunarMonthDays($lunarYear, $i);
if ($leapMonth > 0 && $i == $leapMonth) {
$days += self::leapMonthDays($lunarYear);
}
}
if ($isLeap && $leapMonth == $lunarMonth) {
$days += self::leapMonthDays($lunarYear);
}
$days += $lunarDay - 1;
// 基准日期
$baseDate = new DateTime('1900-01-31');
$baseDate->add(new DateInterval("P{$days}D"));
return [
'year' => (int)$baseDate->format('Y'),
'month' => (int)$baseDate->format('m'),
'day' => (int)$baseDate->format('d')
];
}
/**
* 计算农历年的总天数
*/
private static function lunarYearDays($year) {
$sum = 348; // 12个月平均天数
for ($i = 0x8000; $i > 0x8; $i >>= 1) {
$sum += (self::$lunarInfo[$year - 1900] & $i) ? 1 : 0;
}
return $sum + self::leapMonthDays($year);
}
/**
* 计算闰月天数
*/
private static function leapMonthDays($year) {
if (self::leapMonth($year)) {
return (self::$lunarInfo[$year - 1900] & 0x10000) ? 30 : 29;
}
return 0;
}
/**
* 判断闰月
*/
private static function leapMonth($year) {
return self::$lunarInfo[$year - 1900] & 0xf;
}
/**
* 计算农历月天数
*/
private static function lunarMonthDays($year, $month) {
return (self::$lunarInfo[$year - 1900] & (0x10000 >> $month)) ? 30 : 29;
}
}
// 使用示例
// 公历转农历
$lunarResult = LunarCalendar::solarToLunar(2024, 1, 15);
echo "农历日期:{$lunarResult['yearName']} {$lunarResult['shengXiao']} {$lunarResult['monthName']}{$lunarResult['dayName']}\n";
// 农历转公历
$solarResult = LunarCalendar::lunarToSolar(2024, 1, 15);
echo "公历日期:{$solarResult['year']}-{$solarResult['month']}-{$solarResult['day']}\n";
方法3:使用简单的算法实现
如果不希望依赖第三方库,这里有一个更轻量级的实现:
<?php
class SimpleLunar {
private static $lunarInfo = [
// 1900-2100年农历数据(每个数据表示该年的农历信息)
0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2
];
public static function solarToLunar($year, $month, $day) {
// 获取年份数据
$yearData = self::$lunarInfo[$year - 1900];
// 计算正月初一对应的公历日期
$baseDate = strtotime("1900-01-31");
$targetDate = strtotime("$year-$month-$day");
$offset = floor(($targetDate - $baseDate) / 86400);
// 计算农历年月日
$lunarYear = 1900;
$lunarMonth = 1;
$lunarDay = 1;
while ($offset > 0) {
$yearDays = self::getLunarYearDays($lunarYear);
if ($offset < $yearDays) {
break;
}
$offset -= $yearDays;
$lunarYear++;
}
// 获取闰月信息
$leapMonth = self::getLeapMonth($lunarYear);
for ($i = 1; $i <= 12; $i++) {
$monthDays = self::getLunarMonthDays($lunarYear, $i);
if ($offset < $monthDays) {
$lunarMonth = $i;
$lunarDay = $offset + 1;
break;
}
$offset -= $monthDays;
}
return [
'year' => $lunarYear,
'month' => $lunarMonth,
'day' => $lunarDay,
'isLeap' => false
];
}
private static function getLunarYearDays($year) {
$sum = 348;
for ($i = 0x8000; $i > 0x8; $i >>= 1) {
$sum += (self::$lunarInfo[$year - 1900] & $i) ? 1 : 0;
}
return $sum + self::getLeapMonthDays($year);
}
private static function getLeapMonthDays($year) {
if (self::getLeapMonth($year)) {
return (self::$lunarInfo[$year - 1900] & 0x10000) ? 30 : 29;
}
return 0;
}
private static function getLeapMonth($year) {
return self::$lunarInfo[$year - 1900] & 0xf;
}
private static function getLunarMonthDays($year, $month) {
return (self::$lunarInfo[$year - 1900] & (0x10000 >> $month)) ? 30 : 29;
}
}
推荐方案
- 生产环境:推荐使用
overtrue/lunar库,功能完整且经过测试 - 简单项目:可以使用自定义实现
- 性能要求高:考虑使用缓存或数据库存储农历数据
选择哪种方案取决于你的具体需求,如果需要完整的农历功能(如节气、节日等),建议使用成熟的第三方库。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播