PHP 实现的简单算法来计算公历和农历对应日期
PHP 实现的简单算法来计算公历和农历对应日期:
<?php
function gregorianToChineseDate($year, $month, $day) {
$lunarCalendar = new LunarCalendar();
return $lunarCalendar->gregorianToLunar($year, $month, $day);
}
class LunarCalendar {
private $lunarMonths = [
"正月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "冬月", "腊月"
];
private $lunarDays = [
"初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十",
"十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十",
"廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十"
];
public function gregorianToLunar($year, $month, $day) {
$jd = $this->gregorianToJulianDay($year, $month, $day);
list($lunarYear, $lunarMonth, $lunarDay) = $this->julianDayToLunar($jd);
return "{$lunarYear}年{$this->lunarMonths[$lunarMonth - 1]}{$this->lunarDays[$lunarDay - 1]}";
}
private function gregorianToJulianDay($year, $month, $day) {
if ($month <= 2) {
$year--;
$month += 12;
}
$a = floor($year / 100);
$b = 2 - $a + floor($a / 4);
return floor(365.25 * ($year + 4716)) + floor(30.6001 * ($month + 1)) + $day + $b - 1524.5;
}
private function julianDayToLunar($jd) {
$lunarYear = (int)floor(($jd - 2415021.076998695) / 365.24219878);
$leapMonth = $this->findLeapMonth($lunarYear);
$lunarMonth = $this->findLunarMonth($jd, $lunarYear, $leapMonth);
$lunarDay = (int)floor(($jd - $this->julianDayFromLunarDate($lunarYear, $lunarMonth, 1)) + 1);
return [$lunarYear, $lunarMonth, $lunarDay];
}
private function findLeapMonth($year) {
$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, 0x0a5d0, 0x14573, 0x052d0, 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, 0x055c0, 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, 0x04bd7, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45,
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0
];
$bit = 0x010000000000;
$month = $lunarInfo[$year - 1900] & $bit? 13 : 12;
for ($i = 1; $i <= $month; $i++) {
if (($lunarInfo[$year - 1900] & ($bit >> $i)) === 0) {
return $i;
}
}
return -1;
}
private function findLunarMonth($jd, $lunarYear, $leapMonth) {
$monthStartJd = $this->julianDayFromLunarDate($lunarYear, 1, 1);
$nextYearStartJd = $this->julianDayFromLunarDate($lunarYear + 1, 1, 1);
for ($month = 1; $month <= 12; $month++) {
if ($jd >= $monthStartJd && $jd < ($month == $leapMonth? $monthStartJd + 29.5 : $monthStartJd + 29.5 * ($month % 2 === 0? 1 : 2))) {
return $month;
}
$monthStartJd += ($month % 2 === 0? 1 : 2) * 29.5;
}
return -1;
}
private function julianDayFromLunarDate($year, $month, $day) {
$jd = $this->gregorianToJulianDay(1900, 1, 31);
$lunarYearDiff = $year - 1900;
$leapMonthCount = $this->findLeapMonth($year) > 0? $lunarYearDiff / 19 * 7 : 0;
$normalMonths = ($lunarYearDiff * 12 + ($month - 1) - $leapMonthCount);
$jd += $normalMonths * 29.5 + $day - 1;
return $jd;
}
}
?>
使用方法:
$gregorianDate = [2024, 9, 18];
$lunarDate = gregorianToChineseDate($gregorianDate[0], $gregorianDate[1], $gregorianDate[2]);
echo $lunarDate;
这个算法只是一个简单的实现,可能存在一定的误差,并且没有考虑所有的天文和历法规则的复杂性。实际的农历计算需要更精确的算法和更多的考虑因素。