PHP项目农历转换如何公历互转农历日期时间

wen PHP项目 25

本文目录导读:

PHP项目农历转换如何公历互转农历日期时间

  1. 方法1:使用第三方库(推荐)
  2. 方法2:自定义函数实现
  3. 方法3:使用简单的算法实现
  4. 推荐方案

在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;
    }
}

推荐方案

  1. 生产环境:推荐使用 overtrue/lunar 库,功能完整且经过测试
  2. 简单项目:可以使用自定义实现
  3. 性能要求高:考虑使用缓存或数据库存储农历数据

选择哪种方案取决于你的具体需求,如果需要完整的农历功能(如节气、节日等),建议使用成熟的第三方库。

抱歉,评论功能暂时关闭!