今年年初替公司给客户寄新年贺卡,落款我写了"丙午马年大吉",寄出的日子还在 2 月上旬。一位客户回得很客气:"我们这边蛇年还没过完呢。"我一度以为是他记错。真正让我改习惯的是另一件事:亲戚家孩子生在 2 月 10 日,家里老人一口咬定属蛇,产房发的纪念手环上,年份已经翻到马。
后来我把干支的算法自己推了一遍,才发现这两件事不是谁记错,而是"哪一天算换年"本来就有两套人在用:2026 年的正月初一是 2 月 17 日,丙午年从那天起算。下面这些数我是逐个取模算过的。
为什么是六十,而不是一百二十
十天干:甲乙丙丁戊己庚辛壬癸,十二地支:子丑寅卯辰巳午未申酉戌亥。把它们直接两两相乘是 120 种,但实际只有 60 种能出现,因为干的序号和支的序号必须同为阳或同为阴(甲只能配子寅辰午申戌,乙只能配丑卯巳未酉亥)。我拿两层循环数过:同阴阳的正好 60 个。
另一条路是求公倍数:干每 10 年转一圈、支每 12 年转一圈,要两个同时回到起点得走 lcm(10,12)=60 步。所以"六十甲子"不是凑出来的吉利数,是 10 和 12 的循环周期决定的:干支纪年六十年一循环,生肖只跟着地支走,十二年一轮回。1984 年是甲子,下一个甲子就是 2044 年。
年份取模:两种写法,同一个结果
最常见的是从 0 开始数:干序号 =(年份-4)mod 10,支序号 =(年份-4)mod 12,0 对应甲和子。也有资料写成(年份-3),那是从 1 开始数,余数 0 要归到第 10 个干(癸)和第 12 个支(亥)。两条式子答案完全相同,差别只在数法;我一开始把两套下标混着用,算出来的年份全错一位。
| 公历年 | (年-4)mod 10 | (年-4)mod 12 | 干支 | 生肖 | 六十甲子序号 |
|---|---|---|---|---|---|
| 1900 | 6 | 0 | 庚子 | 鼠 | 37 |
| 1949 | 5 | 1 | 己丑 | 牛 | 26 |
| 1984 | 0 | 0 | 甲子 | 鼠 | 1 |
| 2024 | 0 | 4 | 甲辰 | 龙 | 41 |
| 2025 | 1 | 5 | 乙巳 | 蛇 | 42 |
| 2026 | 2 | 6 | 丙午 | 马 | 43 |
| 2027 | 3 | 7 | 丁未 | 羊 | 44 |
这里有两处专出错的边角。第一处是余数为 0:1900 年(年-4)mod 12 得 0,下标 0 是"子",(年-4)mod 10 得 6,下标 6 是"庚",合起来是庚子年;1984 年两个余数都是 0,正好是六十甲子第一位甲子。第二处是负数取模:JS 的 % 运算符在被除数为负时返回负余数,所以算公元 4 年以前的年份,(年份-4)先得到负数,要写成(余数+10)再 mod 10 才能落回 0 到 9。这一步只是算法层面的防御,1900 年以后(年份-4)恒为正,取模不可能出负数。
要查具体日子我不再手推,用 天干地支计算器:输入公历日期,年柱、月柱、日柱、时柱四柱一起给,附带生肖,还把取模过程写出来,方便我对草稿看哪一步偏了。
生肖和干支年对不上,是因为有两个"新年"
关键在于分界日。民俗和历书按正月初一(也就是春节)换年,国标 GB/T 33661-2017《农历的编算和颁行》也是从正月初一算新年;命理和八字按立春换年,立春在公历 2 月 3 日到 5 日之间浮动。春节的落点却能在 1 月下旬到 2 月中旬之间来回挪——我那份 2024 到 2030 年的表里,最早 1 月 26 日,最晚 2 月 17 日,相差 22 天。于是两个"新年"之间总有一段窗口期,同一日期在两种口径下分属两年。
亲戚家孩子那个 2 月 10 日就落在窗口里:2026 年立春在 2 月上旬、春节要到 2 月 17 日,所以按立春口径已是丙午年、属马,按春节口径还在乙巳年、属蛇,两边都没算错。反方向的窗口也有:2023 年春节 1 月 22 日、立春要到 2 月 4 日,1 月 28 日出生的人按春节口径是癸卯属兔,按立春口径仍是壬寅属虎——早出生十几天就差一个属相。
所以我的选择是看用途:写拜年话、跟长辈说属相走春节口径,排四柱、看年柱走立春口径。有个近似要拎出来:立春并不总在 2 月 4 日,2025 年是 2 月 3 日 22:10。按"2 月 4 日之前都算上一年"整切,多数年份没事;但碰上 2025 年这种交节在 2 月 3 日深夜的,22:10 到 24 点之间出生的人就被归到上一个年柱,差的是不到两小时。要精确到某一天,得按当年立春的实际时刻定,别信写死的 2 月 4 日。
还有一件前置工作比公式更容易出错:先确认那一天到底落在哪个公历日期上。跨时区出生的、只记得农历生辰的,我先把日期定下来再谈干支,比如用 世界时间 核对出生地当天的日期,或用 时间戳转换 把日期换成日序数,免得自己数的天数差一天。
干支纪日是另一条独立的轴
这点特别容易被忽略:纪年六十年一循环、生肖十二年一圈、纪月跟着节气走、纪日是连续六十天不停的一圈,不跟月初、年节、节日对齐。所以同一个人的年柱和日柱之间没有推导关系,四柱是四条各自独立推进的序列——月干由年干起(五虎遁),时干由日干起(五鼠遁),日柱只能靠锚点日期推。
正因为靠锚点,纪日对"起点差一天"极其敏感:锚点定错,整条序列就整体平移一位,结果看上去仍然像一个合法的干支。我能验证的是循环本身:60 的整数倍必然同柱,2026 年 10 月 15 日到 2026 年 12 月 14 日正好 60 天,这两天日柱相同;1900 年 1 月 31 日到 2024 年 2 月 10 日相隔 45300 天,45300 除以 60 得 755 整轮,也必然同柱。
锚点这块我自己串过行,正确值写在这儿:1900 年 1 月 1 日是甲戌日、六十甲子第 11 位;1900 年 1 月 31 日不是甲戌,是甲辰日、第 41 位,上面那组 45300 天两端同为甲辰,正好印证。另两个我逐个核过的是 1949 年 10 月 1 日甲子日(第 1 位)、2026 年 10 月 15 日壬戌日(第 59 位)。通式拿儒略日数写最省事:(JDN+49) mod 60,余数 0 是甲子;这三个点的余数应是 10、0、58,差 1 就整条平移 1 位。
Q:本命年到底按春节还是按立春算?
看你用在哪。民俗的"过本命年"从正月初一起算,命理、八字的年柱从立春起算。差别只在 1 月下旬到 2 月中旬那段窗口,其他日期两种数法结果一样。
Q:为什么网上的干支日和黄历上写的不一样?
因为锚点和日界不同。循环本身是确定的,各家用的起点日期、把子时算在哪一天未必一致,差一两天就体现成干支差一二位。核对时先看它给的锚点或示例日期,别只比结果。
Q:生肖是十二个,干支年有六十个,怎么对应?
生肖只跟着地支,12 年一圈;干支年要把干和支同时对齐,60 年才一圈。所以一个属相对应五个干支年,1984、1996、2008、2020、2032 都属鼠,分别是甲子、丙子、戊子、庚子、壬子。
Q:能不能用(年份-4)mod 10 去算日和月?
不能。年柱可以用年份的线性偏移,月要按节气切,日只能靠日序数取模。这三条轴的起点各不相同,套同一个公式一定会错。
那张贺卡的落款我最后改成了两种:给国内客户的写属相配公历年份,给讲究择日的合作方按立春口径写干支——问的人按哪个日子换年,我就按哪个日子算。