2020-07-14 15:38:51 +08:00
package calendar
import (
2025-09-15 20:55:38 +08:00
"fmt"
"math"
"regexp"
"strconv"
"strings"
2020-07-14 15:38:51 +08:00
"time"
"b612.me/astro/basic"
)
2025-09-15 20:55:38 +08:00
var tiangan = [ ] string { "甲" , "乙" , "丙" , "丁" , "戊" , "己" , "庚" , "辛" , "壬" , "癸" }
var dizhi = [ ] string { "子" , "丑" , "寅" , "卯" , "辰" , "巳" , "午" , "未" , "申" , "酉" , "戌" , "亥" }
2025-09-15 23:40:09 +08:00
func getCst ( ) * time . Location {
return time . FixedZone ( "CST" , 8 * 3600 )
}
2020-07-14 15:38:51 +08:00
const (
JQ_春分 = 15 * iota
JQ_清明
JQ_谷雨
JQ_立夏
JQ_小满
JQ_芒种
JQ_夏至
JQ_小暑
JQ_大暑
JQ_立秋
JQ_处暑
JQ_白露
JQ_秋分
JQ_寒露
JQ_霜降
JQ_立冬
JQ_小雪
JQ_大雪
JQ_冬至
JQ_小寒
JQ_大寒
JQ_立春
JQ_雨水
JQ_惊蛰
)
2026-05-01 22:38:44 +08:00
// Lunar 公历转农历 / solar to lunar calendar.
2022-05-16 20:42:15 +08:00
// 传入 公历年月日,时区
2020-07-14 15:38:51 +08:00
// 返回 农历月,日,是否闰月以及文字描述
2022-05-16 20:42:15 +08:00
// 按现行农历GB/T 33661-2017算法计算, 推荐使用年限为[1929-3000]年
// 古代由于定朔定气误差此处计算会与古时不符
2026-05-27 16:08:11 +08:00
// Inputs are civil year, month, day, and timezone offset in hours.
// Returns the lunar year, month, day, leap-month flag, and text description.
// The current GB/T 33661-2017 lunar-calendar convention is recommended for years 1929-3000.
// For ancient dates, the result may differ from historical practice because computed new-moon and solar-term reconstructions are approximate for ancient dates.
2025-09-15 20:55:38 +08:00
func Lunar ( year , month , day int , timezone float64 ) ( int , int , int , bool , string ) {
2023-07-23 11:47:36 +08:00
return basic . GetLunar ( year , month , day , timezone / 24.0 )
2022-05-16 20:42:15 +08:00
}
2026-05-01 22:38:44 +08:00
// Solar 农历转公历 / lunar to solar calendar.
2022-05-16 20:42:15 +08:00
// 传入 农历年份,月,日,是否闰月,时区
// 传出 公历时间
// 农历年份用公历年份代替,但是岁首需要使用农历岁首
// 例: 计算己亥猪年腊月三十日对应的公历( 即2020年1月24日)
// 由于农历还未到鼠年, 故应当传入Solar(2019,12,30,false)
// 按现行农历GB/T 33661-2017算法计算, 推荐使用年限为[1929-3000]年
// 古代由于定朔定气误差此处计算会与古时不符
2026-05-27 16:08:11 +08:00
// Inputs are the civil-year proxy of the lunar year, lunar month, lunar day, leap-month flag, and timezone offset in hours.
// Returns the corresponding civil time.
// The lunar year parameter follows the civil year containing the lunar New Year of that cycle.
// For example, the last day of the Ji-Hai year corresponds to 2020-01-24, but should still be passed as `Solar(2019, 12, 30, false, ...)`.
// The current GB/T 33661-2017 lunar-calendar convention is recommended for years 1929-3000.
// For ancient dates, the result may differ from historical practice because computed new-moon and solar-term reconstructions are approximate for ancient dates.
2022-05-16 20:42:15 +08:00
func Solar ( year , month , day int , leap bool , timezone float64 ) time . Time {
jde := basic . GetSolar ( year , month , day , leap , timezone / 24.0 )
zone := time . FixedZone ( "CST" , int ( timezone * 3600 ) )
return basic . JDE2DateByZone ( jde , zone , true )
2020-07-14 15:38:51 +08:00
}
2026-05-01 22:38:44 +08:00
// SolarToLunar 公历转农历 / solar to lunar calendar.
2020-07-14 15:38:51 +08:00
// 传入 公历年月日
2025-09-15 20:55:38 +08:00
// 返回 包含农历信息的Time结构体
// 支持年份:[-103,3000]
// [-103,1912] 按照古代历法提供的农历信息
// (1912,3000]按现行农历GB/T 33661-2017算法计算
2026-05-27 16:08:11 +08:00
// Input is a civil `time.Time`.
// Returns a `Time` value carrying the lunar-calendar information.
// Supported years are [-103, 3000].
// Years [-103, 1912] use the historical-calendar tables included in this package.
// Years (1912, 3000] use the current GB/T 33661-2017 lunar-calendar convention.
2025-09-15 20:55:38 +08:00
func SolarToLunar ( date time . Time ) ( Time , error ) {
return innerSolarToLunar ( date )
}
2026-05-01 22:38:44 +08:00
// SolarToLunarByYMD 公历转农历(按年月日) / solar to lunar calendar by year, month, and day.
2025-10-04 17:00:19 +08:00
// 传入 公历年月日
// 返回 包含农历信息的Time结构体
// 支持年份:[-103,3000]
// [-103,1912] 按照古代历法提供的农历信息
// (1912,3000]按现行农历GB/T 33661-2017算法计算
2026-05-27 16:08:11 +08:00
// Inputs are the civil year, month, and day.
// Returns a `Time` value carrying the lunar-calendar information.
// Supported years are [-103, 3000].
// Years [-103, 1912] use the historical-calendar tables included in this package.
// Years (1912, 3000] use the current GB/T 33661-2017 lunar-calendar convention.
2025-10-04 17:00:19 +08:00
func SolarToLunarByYMD ( year , month , day int ) ( Time , error ) {
return innerSolarToLunarByYMD ( year , month , day )
}
2025-09-15 20:55:38 +08:00
func innerSolarToLunar ( date time . Time ) ( Time , error ) {
2025-10-04 17:00:19 +08:00
date = date . In ( getCst ( ) )
if date . Year ( ) < - 103 || date . Year ( ) > 9999 {
2025-09-15 20:55:38 +08:00
return Time { } , fmt . Errorf ( "日期超出范围" )
}
2026-05-01 22:38:44 +08:00
if err := basic . ValidateCivilDate ( date . Year ( ) , int ( date . Month ( ) ) , float64 ( date . Day ( ) ) ) ; err != nil {
return Time { } , fmt . Errorf ( "公历日期不存在" )
}
2025-09-15 20:55:38 +08:00
if date . Year ( ) <= 1912 {
return innerSolarToLunarHanQing ( date ) , nil
}
if date . Year ( ) < 2400 {
y , m , d , l , desc := rapidLunarModern ( date . Year ( ) , int ( date . Month ( ) ) , date . Day ( ) )
if desc == "无法获取农历信息" {
return Time { } , fmt . Errorf ( "无法获取农历信息" )
}
return transformModenLunar2Time ( date , y , m , d , l , desc ) , nil
}
y , m , d , l , desc := basic . GetLunar ( date . Year ( ) , int ( date . Month ( ) ) , date . Day ( ) , 8.0 / 24.0 )
return transformModenLunar2Time ( date , y , m , d , l , desc ) , nil
}
2025-10-04 17:00:19 +08:00
func innerSolarToLunarByYMD ( year , month , day int ) ( Time , error ) {
if year < - 103 || year > 9999 {
return Time { } , fmt . Errorf ( "日期超出范围" )
}
if month < 1 || month > 12 {
return Time { } , fmt . Errorf ( "月份超出范围" )
}
if day < 1 || day > 31 {
return Time { } , fmt . Errorf ( "日期超出范围" )
}
2026-05-01 22:38:44 +08:00
if err := basic . ValidateCivilDate ( year , month , float64 ( day ) ) ; err != nil {
return Time { } , fmt . Errorf ( "公历日期不存在" )
}
2025-10-04 17:00:19 +08:00
if year <= 1912 {
return innerSolarToLunarHanQingByYMD ( year , month , day , time . Time { } ) , nil
}
if year < 2400 {
y , m , d , l , desc := rapidLunarModern ( year , month , day )
if desc == "无法获取农历信息" {
return Time { } , fmt . Errorf ( "无法获取农历信息" )
}
return transformModenLunar2Time ( time . Date ( year , time . Month ( month ) , day , 0 , 0 , 0 , 0 , getCst ( ) ) , y , m , d , l , desc ) , nil
}
y , m , d , l , desc := basic . GetLunar ( year , month , day , 8.0 / 24.0 )
return transformModenLunar2Time ( time . Date ( year , time . Month ( month ) , day , 0 , 0 , 0 , 0 , getCst ( ) ) , y , m , d , l , desc ) , nil
}
2025-09-15 20:55:38 +08:00
func transformModenLunar2Time ( date time . Time , year , month , day int , leap bool , desc string ) Time {
return Time {
solarTime : date ,
lunars : [ ] LunarTime {
{
solarDate : date ,
year : year ,
month : month ,
day : day ,
leap : leap ,
desc : desc ,
comment : "" ,
ganzhiMonth : commonGanZhiOfMonth ( year , month ) ,
eras : nil ,
} ,
} ,
}
2020-07-14 15:38:51 +08:00
}
2026-05-01 22:38:44 +08:00
// LunarToSolar 农历转公历 / lunar to solar calendar.
2025-09-15 20:55:38 +08:00
// 传入 农历描述,如"二零二零年正月初一","元丰六年十月十二","元嘉二十七年七月庚午日"
// 传出 包含公里农历信息的Time结构体切片
// 传入参数支持如下结构
// 农历年中文描述+农历月中文描述+农历日中文描述
// 农历年中文描述+农历月中文描述+干支日中文描述
// 年号+农历月中文描述+农历日中文描述
// 年号+农历月中文描述+干支日中文描述
// 支持年份:[-103,3000]
2026-05-27 16:08:11 +08:00
// Input is a lunar-date description such as `二零二零年正月初一`, `元丰六年十月十二`, or `元嘉二十七年七月庚午日`.
// Returns all matching `Time` results with both civil and lunar information.
// The parser accepts these forms:
// lunar year text + lunar month text + lunar day text
// lunar year text + lunar month text + sexagenary day text
// era name + lunar month text + lunar day text
// era name + lunar month text + sexagenary day text
// Supported years are [-103, 3000].
2025-09-15 20:55:38 +08:00
func LunarToSolar ( desc string ) ( [ ] Time , error ) {
dates , err := innerParseLunar ( desc )
if err != nil {
return nil , err
}
var results [ ] Time
for _ , v := range dates {
date , err := SolarToLunar ( v )
if err != nil {
return nil , err
}
results = append ( results , date )
}
return results , nil
2020-07-14 15:38:51 +08:00
}
2026-05-01 22:38:44 +08:00
// LunarToSolarSingle 农历转公历(单一结果) / lunar to solar calendar, single result.
//
2025-10-04 17:00:19 +08:00
// Deprecated: 推荐使用LunarToSolarByYMD
2025-09-15 20:55:38 +08:00
// 传入 农历年月日,是否闰月
// 传出 包含公里农历信息的Time结构体
// 支持年份:[-103,3000]
// [-103,1912] 按照古代历法提供的农历信息,注意, 这里农历月份代表的是以当时的历法推定的农历月与正月的距离, 正月为1, 二月为2, 依次类推, 闰月显示所闰月
// (1912,3000]按现行农历GB/T 33661-2017算法计算
2026-05-27 16:08:11 +08:00
// Deprecated: use LunarToSolarByYMD.
// Inputs are lunar year, month, day, and the leap-month flag.
// Returns a `Time` value carrying both civil and lunar information.
// Supported years are [-103, 3000].
// For years [-103, 1912], the lunar month index follows the historical calendar in force at that time, counted from the first month of that year.
// Years (1912, 3000] use the current GB/T 33661-2017 lunar-calendar convention.
2025-09-15 20:55:38 +08:00
func LunarToSolarSingle ( year , month , day int , leap bool ) ( Time , error ) {
2025-10-04 17:00:19 +08:00
return LunarToSolarByYMD ( year , month , day , leap )
}
2026-05-01 22:38:44 +08:00
// LunarToSolarByYMD 农历转公历(按年月日) / lunar to solar calendar by year, month, and day.
2025-10-04 17:00:19 +08:00
// 传入 农历年月日,是否闰月
// 传出 包含公里农历信息的Time结构体
// 支持年份:[-103,3000]
// [-103,1912] 按照古代历法提供的农历信息,注意, 这里农历月份代表的是以当时的历法推定的农历月与正月的距离, 正月为1, 二月为2, 依次类推, 闰月显示所闰月
// (1912,3000]按现行农历GB/T 33661-2017算法计算
2026-05-27 16:08:11 +08:00
// Inputs are lunar year, month, day, and the leap-month flag.
// Returns a `Time` value carrying both civil and lunar information.
// Supported years are [-103, 3000].
// For years [-103, 1912], the lunar month index follows the historical calendar in force at that time, counted from the first month of that year.
// Years (1912, 3000] use the current GB/T 33661-2017 lunar-calendar convention.
2025-10-04 17:00:19 +08:00
func LunarToSolarByYMD ( year , month , day int , leap bool ) ( Time , error ) {
if year < - 103 || year > 9999 {
2025-09-15 20:55:38 +08:00
return Time { } , fmt . Errorf ( "年份超出范围" )
}
if year <= 1912 {
date := rapidSolarHan2Qing ( year , month , day , leap , yearDiffLunar ( year , month , day ) , nil )
return SolarToLunar ( date )
}
if year < 2400 {
date := rapidSolarModern ( year , month , day , leap )
return SolarToLunar ( date )
}
date := Solar ( year , month , day , leap , 8.0 )
return SolarToLunar ( date )
2020-07-14 15:38:51 +08:00
}
2026-05-01 22:38:44 +08:00
// JieQi 节气时刻(北京时间) / solar term instant in Beijing time.
//
// 返回传入年份、节气对应的北京时间节气时间。
2026-05-27 16:08:11 +08:00
// Returns the Beijing-time instant of the requested solar term in the supplied year.
2020-07-14 15:38:51 +08:00
func JieQi ( year , term int ) time . Time {
2020-12-31 09:07:54 +08:00
calcJde := basic . GetJQTime ( year , term )
2020-07-14 15:38:51 +08:00
zone := time . FixedZone ( "CST" , 8 * 3600 )
2021-03-04 17:27:33 +08:00
return basic . JDE2DateByZone ( calcJde , zone , false )
2020-07-14 15:38:51 +08:00
}
2026-05-01 22:38:44 +08:00
// WuHou 物候时刻(北京时间) / pentad instant in Beijing time.
//
// 返回传入年份、物候对应的北京时间物候时间。
2026-05-27 16:08:11 +08:00
// Returns the Beijing-time instant of the requested pentad in the supplied year.
2020-07-14 15:38:51 +08:00
func WuHou ( year , term int ) time . Time {
2026-05-01 22:38:44 +08:00
calcJde := basic . GetWuHouTime ( year , term )
2020-07-14 15:38:51 +08:00
zone := time . FixedZone ( "CST" , 8 * 3600 )
2021-03-04 17:27:33 +08:00
return basic . JDE2DateByZone ( calcJde , zone , false )
2020-07-14 15:38:51 +08:00
}
2024-01-15 17:05:18 +08:00
2025-09-15 20:55:38 +08:00
func rapidLunarModern ( year , month , day int ) ( int , int , int , bool , string ) {
2024-01-15 17:05:18 +08:00
var upper = [ ] uint16 { 32274 , 52242 , 41001 , 30036 , 49204 , 36918 , 25882 , 46101 , 34854 , 22674 , 43026 , 31145 , 51241 , 38964 , 26997 , 47149 , 36885 , 23717 , 44069 , 34258 , 53266 , 41001 , 29036 , 49178 , 37915 , 24875 , 46090 , 34853 , 23698 , 43026 , 31129 , 50229 , 38970 , 26971 , 47126 , 36874 , 24804 , 44068 , 32242 , 52274 , 41013 , 28086 , 48173 , 37909 , 25898 , 46089 , 34852 , 22706 , 43050 , 30189 , 50203 , 38957 , 27989 , 47123 , 35881 , 24788 , 45076 , 32298 , 51258 , 40986 , 29099 , 48170 , 37906 , 25897 , 46121 , 34836 , 21754 , 42038 , 31190 , 50197 , 38949 , 27986 , 48146 , 35881 , 23860 , 44084 , 32309 , 51245 , 39981 , 29093 , 49189 , 37906 , 25897 , 46121 , 35500 , 53274 , 42011 , 30123 , 50218 , 38949 , 27986 , 48146 , 36889 , 23770 , 43066 , 32282 , 52246 , 39978 , 29028 , 49188 , 37938 , 24885 , 45109 , 33846 , 22678 , 42005 , 30186 , 51209 , 39972 , 26994 , 47146 , 35885 , 23853 , 43051 , 32341 , 52242 , 41001 , 29076 , 49172 , 37930 , 25885 , 45082 , 33835 , 22675 , 43026 , 30121 , 50217 , 38964 , 27002 , 46133 , 35862 , 23770 , 44069 , 32466 , 52242 , 41001 , 29108 , 48180 , 36917 , 24950 , 45101 , 33813 , 22674 , 43026 , 31209 , 50217 , 38954 , 26989 , 47131 , 34859 , 23765 , 44068 , 34322 , 52242 , 40985 , 29082 , 48186 , 36890 , 24874 , 45098 , 34852 , 21746 , 42034 , 30197 , 50229 , 37942 , 26966 , 47125 , 35881 , 23828 , 44052 , 32298 , 52266 , 39981 , 28077 , 48171 , 37909 , 24873 , 45097 , 34836 , 22762 , 42010 , 30172 , 50202 , 38955 , 26963 , 47122 , 35881 , 24852 , 43060 , 31290 , 51253 , 39990 , 28058 , 48149 , 37906 , 25897 , 45097 , 33844 , 21686 , 42037 , 30198 , 50221 , 39957 , 29010 , 48146 , 36905 , 24884 , 45098 , 32365 , 52251 , 41003 , 30101 , 49188 , 38930 , 26905 , 47125 , 34842 , 22747 , 43034 , 31210 , 50218 , 39956 , 28018 , 48170 , 35893 , 23866 , 44086 , 34518 , 52245 , 41001 , 30100 , 50196 , 37930 , 25973 , 46124 , 34861 , 22677 , 43029 , 31209 , 51241 , 39956 , 28010 , 48154 , 36892 , 23853 , 44058 , 34507 , 53266 , 41001 , 30100 , 49204 , 37946 , 25946 , 45110 , 34838 , 23754 , 43018 , 31208 , 51240 , 39988 , 27061 , 47149 , 35893 , 24854 , 44053 , 34442 , 53265 , 42024 , 29098 , 49194 , 37933 , 25965 , 45099 , 34837 , 23753 , 44049 , 31192 , 51220 , 39962 , 28059 , 47126 , 35882 , 24852 , 45074 , 31785 , 21652 , 41012 , 29114 , 48181 , 37910 , 25962 , 46121 , 34834 , 22761 , 43049 , 31220 , 50220 , 38957 , 28053 , 48139 , 36901 , 25874 , 46098 , 35433 , 53273 , 42010 , 30125 , 50202 , 38922 , 26917 , 47140 , 36882 , 23769 , 43065 , 31226 , 51254 , 39958 , 29002 , 49162 , 37924 , 24882 , 45106 , 34421 , 53293 , 41005 , 30165 , 50197 , 39945 , 26980 , 47140 , 35882 , 23797 , 43053 , 31277 , 51243 , 40981 , 29001 , 49161 , 37908 , 25898 , 45082 , 34523 , 53270 , 42026 , 30098 , 50194 , 38953 , 27988 , 46132 , 34874 , 23770 , 44054 , 31210 , 51237 , 40978 , 29097 , 48169 , 36916 , 24950 , 45101 , 31797 , 21605 , 42021 , 31186 , 50194 , 38953 , 26988 , 47130 , 34859 , 22765 , 44042 , 32293 , 51236 , 40978 , 29081 , 48185 , 35898 , 24859 , 45078 , 34826 , 21669 , 42020 , 30130 , 50226 , 37941 , 25974 , 46125 , 35861 , 22762 , 44041 , 32228 , 52260 , 39978 , 28077 , 48157 , 36909 , 24853 , 45075 , 33833 , 22676 , 43028 , 31146 , 50234 , 39962 , 26987 , 47146 , 36882 , 24809 , 44073 , 34260 , 52276 , 41014 , 29082 , 49173 , 37926 , 26898 , 46098 , 35497 , 54313 , 43060 , 30197 , 50221 , 38957 , 28005 , 47141 , 36882 , 24809 , 45097 , 32300 , 52250 , 40987 , 29101 , 48170 , 37925 , 26898 , 47122 , 34841 , 22746 , 42042 , 31195 , 50198 , 38954 , 28004 , 48164 , 35890 , 23861 , 44085 , 32310 , 51245 , 40981 , 29098 , 50185 , 37924 , 25970 , 46122 , 34861 , 21614 , 42029 , 31189 , 51218 , 38953 , 27988 , 48148 , 36906 , 23837 , 44058 , 32299 , 52266 , 40978 , 29097 , 49193 , 38932 , 24954 , 45110 , 33846 , 22682 , 42021 , 31186 , 51218 , 39977 , 26996 , 47156 , 35893 , 23862 , 43053 , 32405 , 52261 , 42002 , 29097 , 49193 , 37932 , 25901 , 45083 , 33835 , 22677 , 43044 , 31122 , 51218 , 39961 , 27994 }
var lower = [ ] uint8 { 218 , 184 , 92 , 154 , 152 , 84 , 170 , 168 , 180 , 186 , 184 , 54 , 52 , 148 , 82 , 84 , 168 , 180 , 108 , 110 , 108 , 44 , 150 , 148 , 80 , 106 , 216 , 92 , 94 , 92 , 44 , 40 , 148 , 82 , 180 , 216 , 220 , 184 , 90 , 84 , 40 , 148 , 84 , 168 , 182 , 116 , 180 , 86 , 84 , 42 , 40 , 84 , 106 , 104 , 108 , 174 , 172 , 84 , 84 , 168 , 84 , 212 , 216 , 92 , 92 , 152 , 76 , 84 , 170 , 168 , 180 , 186 , 180 , 52 , 154 , 148 , 74 , 80 , 168 , 180 , 108 , 108 , 46 , 44 , 150 , 148 , 80 , 104 , 216 , 92 , 94 , 92 , 44 , 148 , 148 , 202 , 176 , 216 , 218 , 184 , 88 , 42 , 40 , 148 , 170 , 168 , 182 , 116 , 180 , 86 , 84 , 40 , 84 , 84 , 106 , 232 , 108 , 174 , 172 , 76 , 42 , 168 , 84 , 106 , 216 , 92 , 56 , 152 , 76 , 76 , 168 , 212 , 180 , 186 , 180 , 52 , 150 , 148 , 72 , 168 , 104 , 180 , 182 , 108 , 46 , 44 , 148 , 74 , 72 , 104 , 108 , 220 , 94 , 92 , 44 , 148 , 148 , 200 , 216 , 184 , 184 , 92 , 88 , 42 , 40 , 148 , 170 , 168 , 180 , 186 , 180 , 86 , 84 , 40 , 84 , 84 , 104 , 116 , 108 , 172 , 78 , 76 , 166 , 168 , 84 , 106 , 216 , 92 , 156 , 88 , 76 , 72 , 168 , 212 , 180 , 184 , 58 , 52 , 84 , 74 , 72 , 164 , 104 , 116 , 182 , 108 , 44 , 150 , 148 , 74 , 72 , 88 , 108 , 220 , 92 , 46 , 44 , 148 , 74 , 168 , 212 , 180 , 184 , 92 , 88 , 40 , 148 , 84 , 170 , 168 , 180 , 186 , 180 , 52 , 42 , 168 , 84 , 170 , 104 , 116 , 108 , 172 , 46 , 44 , 164 , 84 , 212 , 106 , 216 , 92 , 92 , 88 , 44 , 164 , 164 , 212 , 218 , 184 , 186 , 180 , 84 , 42 , 72 , 164 , 180 , 108 , 182 , 108 , 172 , 86 , 84 , 40 , 84 , 84 , 108 , 110 , 92 , 174 , 172 , 84 , 42 , 168 , 212 , 218 , 184 , 92 , 172 , 168 , 84 , 84 , 168 , 212 , 180 , 184 , 86 , 52 , 150 , 164 , 84 , 170 , 104 , 116 , 182 , 108 , 46 , 44 , 164 , 82 , 212 , 216 , 108 , 220 , 92 , 44 , 40 , 148 , 164 , 212 , 218 , 184 , 184 , 90 , 84 , 42 , 40 , 164 , 180 , 108 , 116 , 182 , 172 , 84 , 42 , 40 , 84 , 84 , 108 , 110 , 92 , 172 , 86 , 84 , 42 , 168 , 212 , 218 , 184 , 92 , 154 , 152 , 84 , 170 , 168 , 180 , 116 , 184 , 54 , 52 , 148 , 74 , 80 , 168 , 180 , 108 , 174 , 108 , 44 , 150 , 148 , 80 , 106 , 216 , 108 , 220 , 92 , 44 , 40 , 148 , 82 , 180 , 216 , 92 , 184 , 90 , 84 , 40 , 148 , 148 , 168 , 182 , 116 , 182 , 172 , 84 , 42 , 40 , 84 , 170 , 104 , 108 , 174 , 172 , 84 , 84 , 168 , 84 , 212 , 216 , 92 , 92 , 154 , 152 , 84 , 170 , 168 , 180 , 186 , 180 , 54 , 52 , 148 , 74 , 80 , 168 , 180 , 108 , 108 , 46 , 44 , 150 , 148 , 80 , 104 , 216 , 92 , 94 , 92 , 44 , 148 , 148 , 74 , 176 , 216 , 218 , 184 , 88 , 42 , 40 , 148 , 84 , 168 , 182 , 116 , 180 , 86 , 84 , 40 , 148 , 84 , 106 , 232 , 108 , 174 , 172 , 76 , 42 , 168 , 84 , 212 , 216 , 92 , 92 , 152 , 76 , 72 , 168 , 212 , 180 , 186 , 180 , 52 , 150 , 148 , 72 , 168 , 104 , 180 , 108 , 108 , 46 , 44 , 148 , 74 , 72 , 104 , 108 , 220 , 94 , 92 , 44 , 148 }
if year < 1900 || year > 2400 {
2025-09-15 20:55:38 +08:00
return 0 , 0 , 0 , false , "超过日期限制"
2024-01-15 17:05:18 +08:00
}
useGoto := false
recalc :
idx := year - 1900
magic := int32 ( upper [ idx ] ) << 8 + int32 ( lower [ idx ] )
springMonth := ( magic & 0x800000 ) >> 23 + 1
springDay := ( magic & 0x7FFFFF ) >> 18
2024-12-31 13:52:59 +08:00
if ! useGoto && springMonth == int32 ( month ) && springDay == int32 ( day ) {
2025-09-15 20:55:38 +08:00
return year , 1 , 1 , false , "正月初一"
2024-01-15 17:05:18 +08:00
}
if ! useGoto && ( springMonth > int32 ( month ) || ( springMonth == int32 ( month ) && springDay > int32 ( day ) ) ) {
year --
useGoto = true
goto recalc
}
calcYear := year
if useGoto {
calcYear ++
}
2025-09-15 23:40:09 +08:00
target := time . Date ( calcYear , time . Month ( month ) , day , 0 , 0 , 0 , 0 , getCst ( ) )
spring := time . Date ( year , time . Month ( int ( springMonth ) ) , int ( springDay ) , 0 , 0 , 0 , 0 , getCst ( ) )
2024-01-15 17:05:18 +08:00
diffDay := int ( target . Sub ( spring ) . Hours ( ) / 24 )
lunarMonth := 1
totalDay := 0
isLeap := false
leapMonth := int ( uint8 ( magic >> 14 ) & 0xF )
strmonth := [ ] string { "十" , "一" , "二" , "三" , "四" , "五" , "六" , "七" , "八" , "九" , "十" , "冬" , "腊" }
strday := [ ] string { "初" , "十" , "廿" , "三" }
for i := 0 ; i < 13 ; i ++ {
var dayofLunar = 29
2024-01-16 11:30:53 +08:00
if uint8 ( magic & 0x3FFE >> ( 13 - i ) ) & 1 == 1 {
2024-01-15 17:05:18 +08:00
dayofLunar ++
}
if totalDay + dayofLunar > diffDay {
var result string
if isLeap {
result += "闰"
}
if lunarMonth == 1 {
result += "正月"
} else {
result += strmonth [ lunarMonth ] + "月"
}
lday := diffDay - totalDay + 1
if lday == 20 {
result += "二十"
} else if lday == 10 {
result += "初十"
} else {
result += strday [ lday / 10 ] + strmonth [ lday % 10 ]
}
2025-09-15 20:55:38 +08:00
return year , lunarMonth , lday , isLeap , result
2024-01-15 17:05:18 +08:00
}
totalDay += dayofLunar
lunarMonth ++
if lunarMonth - leapMonth == 1 && ! isLeap {
isLeap = true
lunarMonth --
} else {
isLeap = false
}
}
2025-09-15 20:55:38 +08:00
return 0 , 0 , 0 , false , "无法获取农历信息"
2024-01-15 17:05:18 +08:00
}
2025-09-15 20:55:38 +08:00
func rapidSolarModern ( year , month , day int , isLeap bool ) time . Time {
2024-01-15 17:05:18 +08:00
var upper = [ ] uint16 { 32274 , 52242 , 41001 , 30036 , 49204 , 36918 , 25882 , 46101 , 34854 , 22674 , 43026 , 31145 , 51241 , 38964 , 26997 , 47149 , 36885 , 23717 , 44069 , 34258 , 53266 , 41001 , 29036 , 49178 , 37915 , 24875 , 46090 , 34853 , 23698 , 43026 , 31129 , 50229 , 38970 , 26971 , 47126 , 36874 , 24804 , 44068 , 32242 , 52274 , 41013 , 28086 , 48173 , 37909 , 25898 , 46089 , 34852 , 22706 , 43050 , 30189 , 50203 , 38957 , 27989 , 47123 , 35881 , 24788 , 45076 , 32298 , 51258 , 40986 , 29099 , 48170 , 37906 , 25897 , 46121 , 34836 , 21754 , 42038 , 31190 , 50197 , 38949 , 27986 , 48146 , 35881 , 23860 , 44084 , 32309 , 51245 , 39981 , 29093 , 49189 , 37906 , 25897 , 46121 , 35500 , 53274 , 42011 , 30123 , 50218 , 38949 , 27986 , 48146 , 36889 , 23770 , 43066 , 32282 , 52246 , 39978 , 29028 , 49188 , 37938 , 24885 , 45109 , 33846 , 22678 , 42005 , 30186 , 51209 , 39972 , 26994 , 47146 , 35885 , 23853 , 43051 , 32341 , 52242 , 41001 , 29076 , 49172 , 37930 , 25885 , 45082 , 33835 , 22675 , 43026 , 30121 , 50217 , 38964 , 27002 , 46133 , 35862 , 23770 , 44069 , 32466 , 52242 , 41001 , 29108 , 48180 , 36917 , 24950 , 45101 , 33813 , 22674 , 43026 , 31209 , 50217 , 38954 , 26989 , 47131 , 34859 , 23765 , 44068 , 34322 , 52242 , 40985 , 29082 , 48186 , 36890 , 24874 , 45098 , 34852 , 21746 , 42034 , 30197 , 50229 , 37942 , 26966 , 47125 , 35881 , 23828 , 44052 , 32298 , 52266 , 39981 , 28077 , 48171 , 37909 , 24873 , 45097 , 34836 , 22762 , 42010 , 30172 , 50202 , 38955 , 26963 , 47122 , 35881 , 24852 , 43060 , 31290 , 51253 , 39990 , 28058 , 48149 , 37906 , 25897 , 45097 , 33844 , 21686 , 42037 , 30198 , 50221 , 39957 , 29010 , 48146 , 36905 , 24884 , 45098 , 32365 , 52251 , 41003 , 30101 , 49188 , 38930 , 26905 , 47125 , 34842 , 22747 , 43034 , 31210 , 50218 , 39956 , 28018 , 48170 , 35893 , 23866 , 44086 , 34518 , 52245 , 41001 , 30100 , 50196 , 37930 , 25973 , 46124 , 34861 , 22677 , 43029 , 31209 , 51241 , 39956 , 28010 , 48154 , 36892 , 23853 , 44058 , 34507 , 53266 , 41001 , 30100 , 49204 , 37946 , 25946 , 45110 , 34838 , 23754 , 43018 , 31208 , 51240 , 39988 , 27061 , 47149 , 35893 , 24854 , 44053 , 34442 , 53265 , 42024 , 29098 , 49194 , 37933 , 25965 , 45099 , 34837 , 23753 , 44049 , 31192 , 51220 , 39962 , 28059 , 47126 , 35882 , 24852 , 45074 , 31785 , 21652 , 41012 , 29114 , 48181 , 37910 , 25962 , 46121 , 34834 , 22761 , 43049 , 31220 , 50220 , 38957 , 28053 , 48139 , 36901 , 25874 , 46098 , 35433 , 53273 , 42010 , 30125 , 50202 , 38922 , 26917 , 47140 , 36882 , 23769 , 43065 , 31226 , 51254 , 39958 , 29002 , 49162 , 37924 , 24882 , 45106 , 34421 , 53293 , 41005 , 30165 , 50197 , 39945 , 26980 , 47140 , 35882 , 23797 , 43053 , 31277 , 51243 , 40981 , 29001 , 49161 , 37908 , 25898 , 45082 , 34523 , 53270 , 42026 , 30098 , 50194 , 38953 , 27988 , 46132 , 34874 , 23770 , 44054 , 31210 , 51237 , 40978 , 29097 , 48169 , 36916 , 24950 , 45101 , 31797 , 21605 , 42021 , 31186 , 50194 , 38953 , 26988 , 47130 , 34859 , 22765 , 44042 , 32293 , 51236 , 40978 , 29081 , 48185 , 35898 , 24859 , 45078 , 34826 , 21669 , 42020 , 30130 , 50226 , 37941 , 25974 , 46125 , 35861 , 22762 , 44041 , 32228 , 52260 , 39978 , 28077 , 48157 , 36909 , 24853 , 45075 , 33833 , 22676 , 43028 , 31146 , 50234 , 39962 , 26987 , 47146 , 36882 , 24809 , 44073 , 34260 , 52276 , 41014 , 29082 , 49173 , 37926 , 26898 , 46098 , 35497 , 54313 , 43060 , 30197 , 50221 , 38957 , 28005 , 47141 , 36882 , 24809 , 45097 , 32300 , 52250 , 40987 , 29101 , 48170 , 37925 , 26898 , 47122 , 34841 , 22746 , 42042 , 31195 , 50198 , 38954 , 28004 , 48164 , 35890 , 23861 , 44085 , 32310 , 51245 , 40981 , 29098 , 50185 , 37924 , 25970 , 46122 , 34861 , 21614 , 42029 , 31189 , 51218 , 38953 , 27988 , 48148 , 36906 , 23837 , 44058 , 32299 , 52266 , 40978 , 29097 , 49193 , 38932 , 24954 , 45110 , 33846 , 22682 , 42021 , 31186 , 51218 , 39977 , 26996 , 47156 , 35893 , 23862 , 43053 , 32405 , 52261 , 42002 , 29097 , 49193 , 37932 , 25901 , 45083 , 33835 , 22677 , 43044 , 31122 , 51218 , 39961 , 27994 }
var lower = [ ] uint8 { 218 , 184 , 92 , 154 , 152 , 84 , 170 , 168 , 180 , 186 , 184 , 54 , 52 , 148 , 82 , 84 , 168 , 180 , 108 , 110 , 108 , 44 , 150 , 148 , 80 , 106 , 216 , 92 , 94 , 92 , 44 , 40 , 148 , 82 , 180 , 216 , 220 , 184 , 90 , 84 , 40 , 148 , 84 , 168 , 182 , 116 , 180 , 86 , 84 , 42 , 40 , 84 , 106 , 104 , 108 , 174 , 172 , 84 , 84 , 168 , 84 , 212 , 216 , 92 , 92 , 152 , 76 , 84 , 170 , 168 , 180 , 186 , 180 , 52 , 154 , 148 , 74 , 80 , 168 , 180 , 108 , 108 , 46 , 44 , 150 , 148 , 80 , 104 , 216 , 92 , 94 , 92 , 44 , 148 , 148 , 202 , 176 , 216 , 218 , 184 , 88 , 42 , 40 , 148 , 170 , 168 , 182 , 116 , 180 , 86 , 84 , 40 , 84 , 84 , 106 , 232 , 108 , 174 , 172 , 76 , 42 , 168 , 84 , 106 , 216 , 92 , 56 , 152 , 76 , 76 , 168 , 212 , 180 , 186 , 180 , 52 , 150 , 148 , 72 , 168 , 104 , 180 , 182 , 108 , 46 , 44 , 148 , 74 , 72 , 104 , 108 , 220 , 94 , 92 , 44 , 148 , 148 , 200 , 216 , 184 , 184 , 92 , 88 , 42 , 40 , 148 , 170 , 168 , 180 , 186 , 180 , 86 , 84 , 40 , 84 , 84 , 104 , 116 , 108 , 172 , 78 , 76 , 166 , 168 , 84 , 106 , 216 , 92 , 156 , 88 , 76 , 72 , 168 , 212 , 180 , 184 , 58 , 52 , 84 , 74 , 72 , 164 , 104 , 116 , 182 , 108 , 44 , 150 , 148 , 74 , 72 , 88 , 108 , 220 , 92 , 46 , 44 , 148 , 74 , 168 , 212 , 180 , 184 , 92 , 88 , 40 , 148 , 84 , 170 , 168 , 180 , 186 , 180 , 52 , 42 , 168 , 84 , 170 , 104 , 116 , 108 , 172 , 46 , 44 , 164 , 84 , 212 , 106 , 216 , 92 , 92 , 88 , 44 , 164 , 164 , 212 , 218 , 184 , 186 , 180 , 84 , 42 , 72 , 164 , 180 , 108 , 182 , 108 , 172 , 86 , 84 , 40 , 84 , 84 , 108 , 110 , 92 , 174 , 172 , 84 , 42 , 168 , 212 , 218 , 184 , 92 , 172 , 168 , 84 , 84 , 168 , 212 , 180 , 184 , 86 , 52 , 150 , 164 , 84 , 170 , 104 , 116 , 182 , 108 , 46 , 44 , 164 , 82 , 212 , 216 , 108 , 220 , 92 , 44 , 40 , 148 , 164 , 212 , 218 , 184 , 184 , 90 , 84 , 42 , 40 , 164 , 180 , 108 , 116 , 182 , 172 , 84 , 42 , 40 , 84 , 84 , 108 , 110 , 92 , 172 , 86 , 84 , 42 , 168 , 212 , 218 , 184 , 92 , 154 , 152 , 84 , 170 , 168 , 180 , 116 , 184 , 54 , 52 , 148 , 74 , 80 , 168 , 180 , 108 , 174 , 108 , 44 , 150 , 148 , 80 , 106 , 216 , 108 , 220 , 92 , 44 , 40 , 148 , 82 , 180 , 216 , 92 , 184 , 90 , 84 , 40 , 148 , 148 , 168 , 182 , 116 , 182 , 172 , 84 , 42 , 40 , 84 , 170 , 104 , 108 , 174 , 172 , 84 , 84 , 168 , 84 , 212 , 216 , 92 , 92 , 154 , 152 , 84 , 170 , 168 , 180 , 186 , 180 , 54 , 52 , 148 , 74 , 80 , 168 , 180 , 108 , 108 , 46 , 44 , 150 , 148 , 80 , 104 , 216 , 92 , 94 , 92 , 44 , 148 , 148 , 74 , 176 , 216 , 218 , 184 , 88 , 42 , 40 , 148 , 84 , 168 , 182 , 116 , 180 , 86 , 84 , 40 , 148 , 84 , 106 , 232 , 108 , 174 , 172 , 76 , 42 , 168 , 84 , 212 , 216 , 92 , 92 , 152 , 76 , 72 , 168 , 212 , 180 , 186 , 180 , 52 , 150 , 148 , 72 , 168 , 104 , 180 , 108 , 108 , 46 , 44 , 148 , 74 , 72 , 104 , 108 , 220 , 94 , 92 , 44 , 148 }
if year < 1900 || year > 2400 {
return time . Time { }
}
idx := year - 1900
magic := int32 ( upper [ idx ] ) << 8 + int32 ( lower [ idx ] )
springMonth := ( magic & 0x800000 ) >> 23 + 1
springDay := ( magic & 0x7FFFFF ) >> 18
2025-09-15 23:40:09 +08:00
spring := time . Date ( year , time . Month ( int ( springMonth ) ) , int ( springDay ) , 0 , 0 , 0 , 0 , getCst ( ) )
2024-01-15 17:05:18 +08:00
lunarMonth := 1
totalDay := 0
leap := false
leapMonth := int ( uint8 ( magic >> 14 ) & 0xF )
for i := 0 ; i < 13 ; i ++ {
if lunarMonth == month && isLeap == leap {
target := spring . AddDate ( 0 , 0 , totalDay + day - 1 )
return target
}
var dayofLunar = 29
2024-01-16 11:30:53 +08:00
if uint8 ( magic & 0x3FFE >> ( 13 - i ) ) & 1 == 1 {
2024-01-15 17:05:18 +08:00
dayofLunar ++
}
totalDay += dayofLunar
lunarMonth ++
if lunarMonth - leapMonth == 1 && ! leap {
leap = true
lunarMonth --
} else {
leap = false
}
}
return time . Time { }
}
2025-09-15 20:55:38 +08:00
// 中文数字到阿拉伯数字的映射
var chineseNumbers = map [ string ] int {
"元" : 1 , "一" : 1 , "二" : 2 , "三" : 3 , "四" : 4 , "五" : 5 ,
"六" : 6 , "七" : 7 , "八" : 8 , "九" : 9 , "十" : 10 ,
"十一" : 11 , "十二" : 12 , "十三" : 13 , "十四" : 14 , "十五" : 15 ,
"十六" : 16 , "十七" : 17 , "十八" : 18 , "十九" : 19 , "二十" : 20 ,
"廿一" : 21 , "廿二" : 22 , "廿三" : 23 , "廿四" : 24 , "廿五" : 25 ,
"廿六" : 26 , "廿七" : 27 , "廿八" : 28 , "廿九" : 29 , "三十" : 30 , "卅一" : 31 ,
}
var chineseDays = map [ string ] int {
"初一" : 1 , "初二" : 2 , "初三" : 3 , "初四" : 4 , "初五" : 5 ,
"初六" : 6 , "初七" : 7 , "初八" : 8 , "初九" : 9 , "初十" : 10 ,
"十一" : 11 , "十二" : 12 , "十三" : 13 , "十四" : 14 , "十五" : 15 ,
"十六" : 16 , "十七" : 17 , "十八" : 18 , "十九" : 19 , "二十" : 20 ,
"廿一" : 21 , "廿二" : 22 , "廿三" : 23 , "廿四" : 24 , "廿五" : 25 ,
"廿六" : 26 , "廿七" : 27 , "廿八" : 28 , "廿九" : 29 , "三十" : 30 , "卅一" : 31 ,
}
// 中文月份到数字的映射
var chineseMonths = map [ string ] int {
"正" : 1 , "腊" : 12 , "冬" : 11 , "十一" : 11 , "十二" : 12 ,
"一" : 1 , "二" : 2 , "三" : 3 , "四" : 4 , "五" : 5 , "六" : 6 ,
"七" : 7 , "八" : 8 , "九" : 9 , "十" : 10 ,
}
func parseChineseDate ( dateStr string ) ( LunarTime , error ) {
var result LunarTime
var err error
result . desc = dateStr
dateStr = "公元" + dateStr
// 正则表达式匹配日期格式
re := regexp . MustCompile ( ` ^([\p { Han}]+?)([一二三四五六七八九十零〇\d]*?元?)年([\p { Han}\d]+?)月([\p { Han}\d]+?)日?$ ` )
matches := re . FindStringSubmatch ( dateStr )
if len ( matches ) < 5 {
return result , fmt . Errorf ( "无效的日期格式: %s" , dateStr )
}
matches [ 1 ] = strings . TrimPrefix ( matches [ 1 ] , "公元" )
// 提取年号
result . comment = matches [ 1 ]
// 转换年份
if result . comment != "" {
result . year , err = convertChineseNumber ( matches [ 2 ] )
if err != nil {
return result , fmt . Errorf ( "无效的年份: %s" , matches [ 2 ] )
}
} else {
// 直接转换年份
if m , _ := regexp . MatchString ( "\\d+" , matches [ 2 ] ) ; m {
result . year , err = strconv . Atoi ( matches [ 2 ] )
if err != nil {
return result , fmt . Errorf ( "无效的年份: %s" , matches [ 2 ] )
}
} else {
result . year = transfer ( matches [ 2 ] , true )
}
}
// 转换月份
monthStr := matches [ 3 ]
if strings . HasPrefix ( monthStr , "闰" ) {
result . leap = true
monthStr = strings . TrimPrefix ( monthStr , "闰" )
}
if month , ok := chineseMonths [ monthStr ] ; ok {
result . month = month
} else {
if m , _ := regexp . MatchString ( "\\d+" , monthStr ) ; m {
result . month , err = strconv . Atoi ( monthStr )
} else {
// 尝试将月份字符串转换为数字
result . month , err = convertChineseNumber ( monthStr )
}
if err != nil {
return result , fmt . Errorf ( "无效的月份: %s" , monthStr )
}
}
// 转换日期
dayStr := matches [ 4 ]
//判断是否是干支日
if len ( dayStr ) == 6 && strings . ContainsAny ( dayStr , "甲乙丙丁戊己庚辛壬癸子丑寅卯辰巳午未申酉戌亥" ) {
// 临时使用干支月代替
result . ganzhiMonth = dayStr
result . day = 0
return result , nil
}
if day , ok := chineseDays [ dayStr ] ; ok {
result . day = day
} else {
if m , _ := regexp . MatchString ( "\\d+" , dayStr ) ; m {
result . day , err = strconv . Atoi ( dayStr )
if err != nil {
return result , fmt . Errorf ( "无效的日期: %s" , dayStr )
}
return result , nil
}
// 尝试将日期字符串转换为数字
result . day , err = convertChineseNumber ( dayStr )
if err != nil {
return result , fmt . Errorf ( "无效的日期: %s" , dayStr )
}
}
return result , nil
}
// convertChineseNumber 将中文数字转换为阿拉伯数字
func convertChineseNumber ( chineseNum string ) ( int , error ) {
if num , ok := chineseNumbers [ chineseNum ] ; ok {
return num , nil
}
return transfer ( chineseNum , false ) , nil
}
func number2Chinese ( num int , isDirectTrans bool ) string {
chs := [ ] string { "零" , "一" , "二" , "三" , "四" , "五" , "六" , "七" , "八" , "九" }
if isDirectTrans {
var res string
for i := 0 ; i < 4 ; i ++ {
tmp := num / ( int ( math . Pow10 ( 3 - i ) ) )
if tmp == 0 && i == 0 {
continue
}
if tmp < 0 {
res = "负"
num = - num
}
res += chs [ tmp ]
num = num % ( int ( math . Pow10 ( 3 - i ) ) )
}
return res
}
if num < 0 || num > 99 {
return ""
}
if num < 10 {
return chs [ num ]
}
if num == 10 {
return "十"
}
if num < 20 {
return "十" + chs [ num - 10 ]
}
if num % 10 == 0 {
return chs [ num / 10 ] + "十"
}
return chs [ num / 10 ] + "十" + chs [ num % 10 ]
}
func transfer ( msg string , direct bool ) int {
keyMap := map [ rune ] int {
'〇 ' : 0 , '零' : 0 , '一' : 1 , '二' : 2 , '三' : 3 , '四' : 4 , '五' : 5 , '六' : 6 , '七' : 7 , '八' : 8 , '九' : 9 , '十' : 10 , '百' : 100 , '千' : 1000 , '万' : 10000 , '亿' : 100000000 , '两' : 2 , '俩' : 2 ,
}
result := 0
if direct {
for _ , num := range [ ] rune ( msg ) {
if val , match := keyMap [ num ] ; match {
result = result * 10 + val
} else {
return 0
}
}
return result
}
secCache := 0
thrCache := 0
fKWord := map [ rune ] int { '百' : 100 , '千' : 1000 , '万' : 10000 , '亿' : 100000000 }
for _ , num := range [ ] rune ( msg ) {
if _ , match := fKWord [ num ] ; ! match {
if num == '十' && thrCache != 0 {
thrCache *= keyMap [ num ]
} else {
thrCache += keyMap [ num ]
}
} else {
if fKWord [ num ] < 10000 {
secCache += thrCache * fKWord [ num ]
thrCache = 0
} else {
secCache += thrCache
thrCache = 0
if secCache == 0 {
result *= fKWord [ num ]
continue
}
result += secCache * fKWord [ num ]
secCache = 0
}
}
}
result += secCache + thrCache
return result
}
2026-05-01 22:38:44 +08:00
// GanZhiOfYear 年干支 / sexagenary year name.
2025-09-15 20:55:38 +08:00
func GanZhiOfYear ( year int ) string {
2026-05-01 22:38:44 +08:00
return basic . GetGanZhi ( year )
2025-09-15 20:55:38 +08:00
}
2026-05-01 22:38:44 +08:00
// GanZhiOfDay 日干支 / sexagenary day name.
2025-09-15 20:55:38 +08:00
func GanZhiOfDay ( t time . Time ) string {
2025-09-15 23:40:09 +08:00
jde := Date2JDE ( time . Date ( t . Year ( ) , t . Month ( ) , t . Day ( ) , 0 , 0 , 0 , 0 , getCst ( ) ) )
2025-09-15 20:55:38 +08:00
diff := int ( jde - 2451550.5 )
if diff >= 0 {
return tiangan [ diff % 10 ] + dizhi [ diff % 12 ]
}
return tiangan [ ( diff % 10 + 10 ) % 10 ] + dizhi [ ( diff % 12 + 12 ) % 12 ]
}
// 获取每年建寅月的天干
func tianGanIndexForFirstMonth ( year int ) int {
diff := ( year - 1998 ) * 2
if diff >= 0 {
return diff % 10
}
return ( diff % 10 + 10 ) % 10
}
// commonGanZhiOfMonth 返回常规以建寅为正月时,指定农历月份的干支
func commonGanZhiOfMonth ( year , month int ) string {
start := tianGanIndexForFirstMonth ( year )
index := ( start + month - 1 ) % 10
return tiangan [ index ] + dizhi [ ( month + 1 ) % 12 ]
}
func ganZhiOfDayIndex ( t time . Time ) ( int , int ) {
2025-09-15 23:40:09 +08:00
jde := Date2JDE ( time . Date ( t . Year ( ) , t . Month ( ) , t . Day ( ) , 0 , 0 , 0 , 0 , getCst ( ) ) )
2025-09-15 20:55:38 +08:00
diff := int ( jde - 2451550.5 )
if diff >= 0 {
return diff % 10 , diff % 12
}
return ( diff % 10 + 10 ) % 10 , ( diff % 12 + 12 ) % 10
}