php 实现公历转农历函数

这几个月想写个主题,搞点与众不同的功能,想到有的博客刻意地在弱化事件属性,那我岂不是可以用农历表示。

在网上翻了好几天,没找到可以准确的转换的函数,要么报错,要么输出不准确。

既然有 AI,我就尝试用 AI 写函数,但是迭代了几十个版本,输出依然不准确,再换了好几个平台和版本后,迭代出一个转换准确的函数,就分享给大家,我是将这个函数集成到 wordpress 主题之中。

主函数



class LunarDateConverter {
    // 农历数据
    private $lunarInfo = array(
        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,
        0x14b63
    );

    // 农历月份名称
    private $lunarMonthName = array('','正月','二月','三月','四月','五月','六月','七月','八月','九月','十月','冬月','腊月');
    // 农历日名称
    private $lunarDayName = array(
        '','初一','初二','初三','初四','初五','初六','初七','初八','初九','初十',
        '十一','十二','十三','十四','十五','十六','十七','十八','十九','二十',
        '廿一','廿二','廿三','廿四','廿五','廿六','廿七','廿八','廿九','三十'
    );

    // 天干数组
    private $tianGan = array("甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸");
    // 地支数组
    private $diZhi = array("子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥");
    //生肖数组
    private $shenXiong = array("鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪");
    /**
     * 获取农历某一年的天数
     *
     * 将公历日期转换为农历日期
     *
     * @param string $gregorianDate 公历日期,格式为 Y-m-d
     * @return string 农历日期,格式为 二零二五年(乙巳)四月初九
     */
    public function gregorianToLunar($gregorianDate,$hasYear = true,$hastgdz = true,$hashours = true) {
        // 解析公历日期
        $date = strtotime($gregorianDate);
        $year = intval(date('Y', $date));
        $month = intval(date('m', $date));
        $day = intval(date('d', $date));
        $hour = intval(date('H', $date));

        // 公历 1900 年 1 月 31 日为农历 1900 年正月初一
        $startDate = strtotime('1900-01-31');
        $offset = intval(($date - $startDate) / 86400);

        $lunarYear = 1900;
        while ($offset >= 0) {
            $temp = $this->getLunarYearDays($lunarYear);
            if ($offset < $temp) break;
            $offset -= $temp;
            $lunarYear++;
        }

        $lunarLeap = $this->getLunarLeapMonth($lunarYear);
        $lunarLeapFlag = false;

        $lunarMonth = 1;
        while ($lunarMonth <= 12) {
            if ($lunarLeap > 0 && $lunarMonth == ($lunarLeap + 1) && !$lunarLeapFlag) {
                $days = $this->getLunarLeapDays($lunarYear);
                $lunarLeapFlag = true;
            } else {
                $days = $this->getLunarMonthDays($lunarYear, $lunarMonth);
            }

            if ($offset < $days) break;
            $offset -= $days;
            if ($lunarLeapFlag && $lunarMonth == $lunarLeap) $lunarLeapFlag = false;
            if ($lunarMonth == 12 && !$lunarLeapFlag) $lunarYear++;
            $lunarMonth++;
        }

        $lunarDay = $offset + 1;

        // 计算天干索引
        $ganIndex = ($lunarYear - 4) % 10;
        if ($ganIndex < 0) {
            $ganIndex += 10;
        }
        // 计算地支索引
        $zhiIndex = ($lunarYear - 4) % 12;
        if ($zhiIndex < 0) {
            $zhiIndex += 12;
        }
        //属相计算
        $shenxiaoIndex = ($lunarYear - 1900) % 12;
        if ($shenxiaoIndex  < 0) {
            $shenxiaoIndex  += 12;
        }
        
        // 返回天干地支组合
        $tianGandizhi = $this->tianGan[$ganIndex] . $this->diZhi[$zhiIndex].$this->shenXiong[$shenxiaoIndex];

        // 转换年份为中文数字
        $chineseYear = strtr($lunarYear, array(
            '0' => '零', '1' => '一', '2' => '二', '3' => '三', '4' => '四',
            '5' => '五', '6' => '六', '7' => '七', '8' => '八', '9' => '九'
        ));

        return (($hasYear)?$chineseYear:'').(($hastgdz)?'['.$tianGandizhi.'年] ':'')
                . $this->lunarMonthName[$lunarMonth] 
                . $this->lunarDayName[$lunarDay]
                . ($hashours?$this->timeTodizhi($hour):'');
    }

    /**
     * 获取农历某一年的天数
     *
     * @param int $year 农历年份
     * @return int 农历某一年的天数
     */
    private function getLunarYearDays($year) {
        $sum = 348;
        for ($i = 0x8000; $i > 0x8; $i >>= 1) {
            if ($this->lunarInfo[$year - 1900] & $i) $sum += 1;
        }
        return $sum + $this->getLunarLeapDays($year);
    }
    /**
     * 获取转化后的地支时辰
     *
     * @param int $hour 24 小时制时间
     * @return int 地支时辰
     */
    private function timeTodizhi($hour){
        $hour = $hour % 24;//确保时间在0-23之间
        $index = ($hour+1); // 2
        $zhishi = ["子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"];
        return $zhishi[$index%12].'时';
    }

    /**
     * 获取农历某一年闰月的天数
     *
     * @param int $year 农历年份
     * @return int 农历某一年闰月的天数,如果没有闰月则返回 0
     */
    private function getLunarLeapDays($year) {
        if ($this->getLunarLeapMonth($year)) {
            return (($this->lunarInfo[$year - 1900] & 0x10000)? 30 : 29);
        }
        return 0;
    }

    /**
     * 获取农历某一年的闰月是几月,如果没有闰月则返回 0
     *
     * @param int $year 农历年份
     * @return int 农历某一年的闰月月份,如果没有闰月则返回 0
     */
    private function getLunarLeapMonth($year) {
        return ($this->lunarInfo[$year - 1900] & 0xf);
    }

    /**
     * 获取农历某一年某一月的天数
     *
     * @param int $year 农历年份
     * @param int $month 农历月份
     * @return int 农历某一年某一月的天数
     */
    private function getLunarMonthDays($year, $month) {
        return (($this->lunarInfo[$year - 1900] & (0x10000 >> $month))? 30 : 29);
    }
}

使用方式和参数


// 使用示例
$LunarDateConverter = new LunarDateConverter();
//gregorianToLunar($gregorianDate,$hasYear = true,$hastgdz = true,$hashours = true)
$LunarDateConverter->gregorianToLunar(时间戳,[是否显示年(默认显示)],[是否显示天干地支(默认显示)],[是否显示地支时辰(默认显示)])

$LunarDateConverter = new LunarDateConverter();
$lunarDate = $LunarDateConverter->gregorianToLunar('2024-01-15');
echo $lunarDate; // 输出:二零二四年(丙戌)正月初一

我封装了个函数在 wordpres 中使用,将下述函数和上面的主函数放到 wordpress 主题的 functions.php 中,


function lunar_data($d = null,$hasYear = true,$hastgdz = true,$hashours = true) {
    $LunarDateConverter = new LunarDateConverter();   
    echo $LunarDateConverter->gregorianToLunar($d,$hasYear,$hastgdz,$hashours);
}

在稿件中使用

<?php lunar_data(get_the_time('Y-m-d H:m:s'),false); ?>

在评论中使用

<?php lunar_data(get_comment_time('Y-m-d  h:m:s'),false,false);?>

函数转换的时间段为 从 1900 年到 2050 年(好像是 2050年之前,再往后也没意义

ok,到此结束!如有问题,到我的博客去留言。

0 thoughts on “php 实现公历转农历函数
添加一条新回复 回到顶部

亲爱的,您必须 登录后方可留言。