chinese-lunar-date 使用手册

版本:1.0.0 | 最后更新:2025-06 Copyright (c) 2025 北京锋通科技有限公司 (郭玉峰, 吴琼). MIT License.


目录


1. 概述

chinese-lunar-date 是一个基于天文算法的高精度农历历法库,提供以下核心能力:

能力 说明
公历 ↔ 农历转换 基于 VSOP87 行星理论的天文级精度
天干地支 年柱、月柱、日柱,六十甲子序号与字符串互转
生肖 12 生肖多语言名称 + Emoji
24 节气 精确到天的节气计算,6 种语言
传统节日 13 个内置节日,按地区自动筛选
格式化 5 种内置格式 + 自定义模式串(17 种 Token)
多历法 中国 / 日本 / 韩国 / 越南四种农历变体
多语言 简体中文 / 繁體中文(台灣/香港)/ 日本語 / 한국어 / Tiếng Việt

输出格式: ESM / CJS / IIFE,IIFE 压缩后仅 ~47 KB(gzip ~16 KB)。


2. 安装与引入

2.1 安装

npm install chinese-lunar-date

2.2 ESM 引入

import { ChineseDate, formatGanZhi, PATTERNS } from 'chinese-lunar-date'

2.3 CJS 引入

const { ChineseDate, formatGanZhi, PATTERNS } = require('chinese-lunar-date')

2.4 浏览器 IIFE 引入

<script src="dist/chinese-lunar-date.iife.js"></script>
<script>
  // 所有 API 挂载在全局 ChineseDate 对象上
  const date = ChineseDate.ChineseDate.fromGregorian(2024, 6, 21)
  console.log(date.formatFull())  // "甲辰龙年 五月初六"

  // 低级 API
  const lunar = ChineseDate.gregorianToLunar(ChineseDate.CHINESE_CONFIG, 2024, 6, 21)
  console.log(lunar)  // { cycle: 78, year: 34, month: 5, leap: false, day: 6 }
</script>

2.5 TypeScript 支持

本库使用原生 TypeScript 编写,自带完整类型定义,无需额外安装 @types 包。

import type { LunarDateValue, GanZhiPair, LocaleCode } from 'chinese-lunar-date'

3. 核心概念

3.1 六十甲子周期(cycle / year)

中国农历使用连续的六十甲子周期(六十甲子)来标记年份:

公历年份换算公式:

gregorianYear = epochYear + (cycle - 1) * 60 + (year - 1)
// 例:中国历法 epoch = -2636,cycle=78, year=34 → 2024

提示:大多数场景下,直接使用 date.gregorianYear 属性即可获取公历年份,无需手动计算。

3.2 不可变值对象

所有日期实例(ChineseDateJapaneseDate 等)都是不可变的。任何修改操作都会返回新实例,原实例保持不变:

const date = ChineseDate.fromGregorian(2024, 6, 21)
const tomorrow = date.addDays(1)  // 返回新实例

date === tomorrow       // false
date.day                // 6(原实例未变)
tomorrow.day            // 7

3.3 历法配置与地区差异

四种历法变体使用不同的时区历史和纪元:

历法 时区 纪元 配置常量
中国 UTC+8 公元前 2636 年 CHINESE_CONFIG
日本 UTC+9 公元前 2636 年 JAPANESE_CONFIG
韩国 UTC+8:30 / UTC+9 公元前 2333 年(檀君) KOREAN_CONFIG
越南 UTC+7 / UTC+8 公元前 2636 年 VIETNAMESE_CONFIG

3.4 语言与地区代码

代码 语言 说明
zh-CN 简体中文 中国大陆
zh-TW 繁體中文 台灣
zh-HK 繁體中文 香港
ja-JP 日本語 日本
ko-KR 한국어 韩国
vi-VN Tiếng Việt 越南

4. 创建日期

4.1 从公历创建

const date = ChineseDate.fromGregorian(2024, 6, 21)
// { cycle: 78, year: 34, month: 5, leap: false, day: 6 }
// 即:甲辰年 五月初六

参数为公历年、月(1-12)、日。

4.2 从 JavaScript Date 创建

const jsDate = new Date(2024, 5, 21)  // 注意:JS Date 月份从 0 开始
const date = ChineseDate.fromDate(jsDate)

注意fromDate 使用本地时区提取日期分量。

4.3 从 LunarDateValue 创建

适用于从序列化数据恢复实例:

const date = ChineseDate.fromValue({ cycle: 78, year: 34, month: 5, leap: false, day: 6 })

4.4 从字符串解析

支持三种公历日期格式:

// ISO 格式
const d1 = ChineseDate.fromISO('2024-06-21')

// 斜杠格式
const d2 = ChineseDate.fromISO('2024/06/21')

// 中文格式
const d3 = ChineseDate.fromISO('2024年6月21日')

// 解析失败返回 null
const d4 = ChineseDate.fromISO('invalid')  // null

// parse 是 fromISO 的别名
const d5 = ChineseDate.parse('2024-06-21')

4.5 从时间戳创建

const date = ChineseDate.fromTimestamp(Date.now())

4.6 从儒略日创建

const date = ChineseDate.fromJDE(2460482.5)

4.7 获取今天

const today = ChineseDate.today()

5. 读取属性

5.1 农历日期属性

const date = ChineseDate.fromGregorian(2024, 6, 21)

date.cycle           // 78      — 六十甲子周期序号
date.year            // 34      — 周期内年序(34 → 甲辰)
date.month           // 5       — 五月
date.leap            // false   — 非闰月
date.day             // 6       — 初六
date.calendarType    // 'chinese'
date.gregorianYear   // 2024

5.2 天干地支属性

date.yearGanZhi        // "甲辰"   — 年干支字符串
date.monthGanZhi       // "庚午"   — 月干支字符串
date.dayGanZhi         // "甲寅"   — 日干支字符串

date.yearGanZhiPair    // { gan: 0, zhi: 4 }  — 年干支索引对
date.monthGanZhiPair   // { gan: 6, zhi: 6 }  — 月干支索引对
date.dayGanZhiPair     // { gan: 0, zhi: 2 }  — 日干支索引对

date.ganZhiInfo        // { year: {gan:0,zhi:4}, month: {gan:6,zhi:6}, day: {gan:0,zhi:2} }

干支索引说明:

5.3 生肖属性

date.zodiac        // "龙"     — 生肖(简体中文)
date.zodiacEmoji   // "🐉"    — 生肖 Emoji
date.zodiacIndex   // 4        — 生肖索引(0=鼠, 1=牛, ..., 11=猪)

date.getZodiac('zh-TW')  // "龍"
date.getZodiac('ja-JP')  // "辰"
date.getZodiac('ko-KR')  // "용"
date.getZodiac('vi-VN')  // "Thìn"

5.4 公历与星期属性

date.weekday           // 5        — 星期几(0=周日, 1=周一, ..., 6=周六)
date.weekdayName       // "星期五"  — 星期全称(简体中文)
date.weekdayShort      // "五"     — 星期简称(简体中文)

date.getWeekdayName('ja-JP')   // "金曜日"
date.getWeekdayShort('ja-JP')  // "金"
date.getWeekdayName('ko-KR')   // "금요일"
date.getWeekdayShort('vi-VN')  // "T6"

date.gregorianYearCn      // "二〇二四"  — 公历年中文数字(〇版)
date.gregorianYearCnLower // "二零二四"  — 公历年中文数字(零版)

6. 日期转换

6.1 转为公历

const date = ChineseDate.fromGregorian(2024, 6, 21)

date.toGregorian()  // { year: 2024, month: 6, day: 21 }

6.2 转为 JavaScript Date

date.toDate()  // Date object (使用本地时区)

6.3 序列化与反序列化

// 序列化
const json = date.toJSON()
// { cycle: 78, year: 34, month: 5, leap: false, day: 6 }

const str = JSON.stringify(date)
// '{"cycle":78,"year":34,"month":5,"leap":false,"day":6}'

// 反序列化
const restored = ChineseDate.fromValue(JSON.parse(str))

7. 日期运算

7.1 日期加减

所有日期运算方法都返回新实例,原实例不变:

const date = ChineseDate.fromGregorian(2024, 6, 21)

// 加减天数
const tomorrow = date.addDays(1)
const yesterday = date.addDays(-1)
const nextWeek = date.addDays(7)

// 加减月数(基于公历计算,自动处理月末 clamp)
const nextMonth = date.addMonths(1)
const lastMonth = date.addMonths(-1)

// 加减年数(基于公历计算,自动处理闰年 clamp)
const nextYear = date.addYears(1)
const lastYear = date.addYears(-1)

注意addMonthsaddYears 在公历上运算。如果结果日期无效(如 2 月 30 日),会自动 clamp 到该月最后一天。

7.2 修改字段

使用 with() 修改部分字段,返回新实例:

const date = ChineseDate.fromGregorian(2024, 6, 21)

const firstDay = date.with({ day: 1 })     // 五月初一
const prevMonth = date.with({ month: 4 })   // 四月初六
const leapMonth = date.with({ month: 4, leap: true })  // 闰四月初六

7.3 日期比较

const a = ChineseDate.fromGregorian(2024, 6, 21)
const b = ChineseDate.fromGregorian(2024, 6, 20)

a.equals(b)      // false
a.isAfter(b)     // true
a.isBefore(b)    // false

a.equals(a)      // true

注意equals 同时比较 calendarType,不同历法的实例即使日期相同也不相等。

7.4 日期差值

const a = ChineseDate.fromGregorian(2024, 6, 21)
const b = ChineseDate.fromGregorian(2024, 6, 18)

a.diffDays(b)  // 3
b.diffDays(a)  // -3

7.5 月天数查询

const date = ChineseDate.fromGregorian(2024, 6, 21)
date.daysInMonth()  // 29 或 30(农历月只有大月30天和小月29天)

8. 格式化

8.1 内置格式方法

const date = ChineseDate.fromGregorian(2024, 6, 21)

// 完整格式
date.formatFull()          // "甲辰龙年 五月初六"
date.formatFull('zh-TW')   // "甲辰龍年 五月初六"
date.formatFull('zh-HK')   // "甲辰龍年 五月初六"
date.formatFull('ja-JP')   // "甲辰辰年 五月初六"

// 干支格式
date.formatGanZhi()        // "甲辰年 庚午月 甲寅日"
date.formatGanZhi('ko-KR') // "甲辰年 庚午月 甲寅日"

// 紧凑格式(适合存储和传输)
date.formatCompact()       // "78/34/5/0/6"

// ISO 格式
date.formatISO()           // "78-34-5-0-6"

// 默认字符串(等同 formatFull)
date.toString()            // "甲辰龙年 五月初六"

紧凑格式说明cycle/year/month/leap/day,其中 leap 编码为 0(非闰月)或 1(闰月)。

8.2 自定义模式

const date = ChineseDate.fromGregorian(2024, 6, 21)

// 农历模式
date.format('{Ygz}{Z}年 {Mname}{Dname}', 'zh-CN')   // "甲辰龙年 五月初六"
date.format('{Ygz}{Z}年 {Mname}{Dname}', 'zh-TW')   // "甲辰龍年 五月初六"

// 公历模式(需要公历数据,值对象的 format 方法自动提供)
date.format('{GY}年{GM}月{GD}日', 'zh-CN')           // "2024年6月21日"
date.format('{GYcn}年{GMcn}月{GDcn}日', 'zh-CN')     // "二〇二四年六月二十一日"
date.format('{GY}-{GM0}-{GD0}', 'zh-CN')             // "2024-06-21"

// 混合模式
date.format('{Ygz}年 {Mname}{Dname} {GY}年{GM}月{GD}日', 'zh-CN')
// "甲辰年 五月初六 2024年6月21日"

// 带星期
date.format('{GYcn}年{GMcn}月{GDcn}日 {Wname}', 'zh-CN')
// "二〇二四年六月二十一日 星期五"

8.3 预设格式常量 PATTERNS

import { PATTERNS } from 'chinese-lunar-date'

const date = ChineseDate.fromGregorian(2025, 10, 11)

date.format(PATTERNS.GREGORIAN_CN_FULL)        // "二〇二五年十月十一日"
date.format(PATTERNS.GREGORIAN_CN_FULL_LOWER)  // "二零二五年十月十一日"
date.format(PATTERNS.GREGORIAN_CN_NUMERIC)     // "2025年10月11日"
date.format(PATTERNS.GREGORIAN_ISO)            // "2025-10-11"
date.format(PATTERNS.GREGORIAN_SLASH)          // "2025/10/11"
date.format(PATTERNS.LUNAR_FULL)               // "甲辰龙年 九月初九"
date.format(PATTERNS.LUNAR_GANZHI)             // "甲辰年 九月初九"
date.format(PATTERNS.FULL_WITH_WEEKDAY)        // "二〇二五年十月十一日 星期六"
date.format(PATTERNS.NUMERIC_WITH_WEEKDAY)     // "2025年10月11日 星期六"
date.format(PATTERNS.LUNAR_WITH_WEEKDAY)       // "甲辰龙年 九月初九 星期六"
常量 格式模式 输出示例
GREGORIAN_CN_FULL {GYcn}年{GMcn}月{GDcn}日 二〇二五年十月十一日
GREGORIAN_CN_FULL_LOWER {GYcn2}年{GMcn}月{GDcn}日 二零二五年十月十一日
GREGORIAN_CN_NUMERIC {GY}年{GM}月{GD}日 2025年10月11日
GREGORIAN_ISO {GY}-{GM0}-{GD0} 2025-10-11
GREGORIAN_SLASH {GY}/{GM}/{GD} 2025/10/11
LUNAR_FULL {Ygz}{Z}年 {Mname}{Dname} 甲辰龙年 九月初九
LUNAR_GANZHI {Ygz}年 {Mname}{Dname} 甲辰年 九月初九
FULL_WITH_WEEKDAY {GYcn}年{GMcn}月{GDcn}日 {Wname} 二〇二五年十月十一日 星期六
NUMERIC_WITH_WEEKDAY {GY}年{GM}月{GD}日 {Wname} 2025年10月11日 星期六
LUNAR_WITH_WEEKDAY {Ygz}{Z}年 {Mname}{Dname} {Wname} 甲辰龙年 九月初九 星期六

8.4 格式化 Token 完整参考

农历相关

Token 输出 示例
{CY} 周期序号 78
{Y} 周期内年序 34
{Ygz} 年干支 甲辰
{Z} 生肖 /
{M} 月份数字 5
{Mname} 月份名称 五月 / 閏四月
{L} 闰月标记 / (非闰月时为空)
{D} 日期数字 6
{Dname} 日期名称 初六 / 廿一 / 三十

公历相关

Token 输出 示例
{GY} 公历年 2024
{GYcn} 公历年中文(〇版) 二〇二四
{GYcn2} 公历年中文(零版) 二零二四
{GM} 公历月 6
{GM0} 公历月补零 06
{GMcn} 公历月中文
{GD} 公历日 21
{GD0} 公历日补零 21
{GDcn} 公历日中文 二十一

星期相关

Token 输出 示例
{W} 星期几数字 (0-6) 5
{Wname} 星期全称 星期五 / 金曜日
{Wshort} 星期简称 /

注意:公历和星期相关 Token 需要公历日期数据。使用值对象的 format() 方法时自动提供;使用独立 format() 函数时需手动传入公历日期。

8.5 解析

解析农历紧凑格式

import { parseCompact, parseISO } from 'chinese-lunar-date'

parseCompact('78/34/5/0/6')    // { cycle: 78, year: 34, month: 5, leap: false, day: 6 }
parseCompact('78/34/2/1/15')   // { cycle: 78, year: 34, month: 2, leap: true, day: 15 }
parseCompact('invalid')        // null

parseISO('78-34-5-0-6')        // { cycle: 78, year: 34, month: 5, leap: false, day: 6 }
parseISO('78-34-2-1-15')       // { cycle: 78, year: 34, month: 2, leap: true, day: 15 }
parseISO('invalid')            // null

解析公历日期字符串

import { parseGregorian } from 'chinese-lunar-date'

parseGregorian('2025-10-11')      // { year: 2025, month: 10, day: 11 }
parseGregorian('2025/10/11')      // { year: 2025, month: 10, day: 11 }
parseGregorian('2025年10月11日')  // { year: 2025, month: 10, day: 11 }
parseGregorian('invalid')         // null

9. 天干地支

9.1 值对象上的干支属性

const date = ChineseDate.fromGregorian(2024, 6, 21)

// 字符串形式
date.yearGanZhi    // "甲辰"
date.monthGanZhi   // "庚午"
date.dayGanZhi     // "甲寅"

// 索引对形式
date.yearGanZhiPair   // { gan: 0, zhi: 4 }
date.monthGanZhiPair  // { gan: 6, zhi: 6 }
date.dayGanZhiPair    // { gan: 0, zhi: 2 }

// 完整信息
date.ganZhiInfo
// { year: {gan:0,zhi:4}, month: {gan:6,zhi:6}, day: {gan:0,zhi:2} }

9.2 独立干支函数

import {
  yearGanZhi, monthGanZhi, dayGanZhi,
  getGanZhiInfo, formatGanZhi, parseGanZhi,
  ganZhiToCycleIndex, allGanZhi,
  ganChar, zhiChar, GAN, ZHI
} from 'chinese-lunar-date'

const value = { cycle: 78, year: 34, month: 7, leap: false, day: 28 }

// 获取干支索引对
yearGanZhi(value)   // { gan: 3, zhi: 9 }  → 丁酉
monthGanZhi(value)  // { gan: ..., zhi: ... }
dayGanZhi(2460482)  // { gan: ..., zhi: ... }  — 注意:日干支需要 JDE

// 格式化
formatGanZhi({ gan: 3, zhi: 9 })  // "丁酉"

// 解析
parseGanZhi('甲子')  // { gan: 0, zhi: 0 }
parseGanZhi('癸亥')  // { gan: 9, zhi: 11 }

// 六十甲子序号
ganZhiToCycleIndex({ gan: 0, zhi: 0 })   // 1  — 甲子是第1个
ganZhiToCycleIndex({ gan: 9, zhi: 11 })  // 60 — 癸亥是第60个

// 天干/地支字符
ganChar(0)  // "甲"
zhiChar(0)  // "子"

// 天干/地支数组
GAN  // ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"]
ZHI  // ["子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"]

9.3 六十甲子表

allGanZhi()
// ["甲子", "乙丑", "丙寅", "丁卯", "戊辰", "己巳", "庚午", "辛未", "壬申", "癸酉",
//  "甲戌", "乙亥", "丙子", ..., "壬戌", "癸亥"]
// 共 60 项

10. 生肖

10.1 值对象上的生肖属性

const date = ChineseDate.fromGregorian(2024, 6, 21)

date.zodiac        // "龙"
date.zodiacEmoji   // "🐉"
date.zodiacIndex   // 4

date.getZodiac('zh-CN')  // "龙"
date.getZodiac('zh-TW')  // "龍"
date.getZodiac('zh-HK')  // "龍"
date.getZodiac('ja-JP')  // "辰"
date.getZodiac('ko-KR')  // "용"
date.getZodiac('vi-VN')  // "Thìn"

10.2 独立生肖函数

import { zodiac, zodiacEmoji, zodiacFromGregorianYear, zodiacList, zodiacIndex } from 'chinese-lunar-date'

const value = { cycle: 78, year: 34, month: 7, leap: false, day: 28 }

// 多语言生肖名称
zodiac(value, 'zh-CN')  // "鸡"
zodiac(value, 'zh-TW')  // "雞"
zodiac(value, 'ja-JP')  // "鶏"
zodiac(value, 'ko-KR')  // "닭"
zodiac(value, 'vi-VN')  // "Dậu"

// Emoji
zodiacEmoji(value)  // "🐔"

// 生肖索引
zodiacIndex(value)  // 9(鸡)

// 从公历年快速获取
zodiacFromGregorianYear(2024, 'zh-CN')  // "龙"
zodiacFromGregorianYear(2024, 'ja-JP')  // "辰"

// 生肖列表
zodiacList('zh-CN')  // ["鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"]
zodiacList('zh-TW')  // ["鼠", "牛", "虎", "兔", "龍", "蛇", "馬", "羊", "猴", "雞", "狗", "豬"]
zodiacList('ko-KR')  // ["쥐", "소", "호랑이", "토끼", "용", "뱀", "말", "양", "원숭이", "닭", "개", "돼지"]

11. 24 节气

11.1 获取节气日期

import { ChineseDate } from 'chinese-lunar-date'

// 获取指定节气的公历日期(节气编号 1-24)
ChineseDate.solarTerm(1, 2024)   // { year: 2024, month: 2, day: 4 }   — 立春
ChineseDate.solarTerm(5, 2024)   // { year: 2024, month: 4, day: 4 }   — 清明
ChineseDate.solarTerm(24, 2024)  // { year: 2024, month: 1, day: 20 }  — 大寒

// 清明快捷方法
ChineseDate.qingming(2024)       // { year: 2024, month: 4, day: 4 }

节气编号对照表:

编号 名称 编号 名称 编号 名称 编号 名称
1 立春 7 立夏 13 立秋 19 立冬
2 雨水 8 小满 14 处暑 20 小雪
3 惊蛰 9 芒种 15 白露 21 大雪
4 春分 10 夏至 16 秋分 22 冬至
5 清明 11 小暑 17 寒露 23 小寒
6 谷雨 12 大暑 18 霜降 24 大寒

奇数编号为节气(节),偶数编号为中气(气)。

11.2 节气名称

import { solarTermName, solarTermNames } from 'chinese-lunar-date'

// 单个节气名称
solarTermName(3, 'zh-CN')  // "惊蛰"
solarTermName(3, 'zh-TW')  // "驚蟄"
solarTermName(3, 'zh-HK')  // "驚蟄"
solarTermName(3, 'ja-JP')  // "啓蟄"
solarTermName(3, 'ko-KR')  // "경칩"
solarTermName(3, 'vi-VN')  // "Kinh trập"

// 全部节气名称
solarTermNames('zh-CN')  // ["立春", "雨水", "惊蛰", ..., "小寒", "大寒"]
solarTermNames('ja-JP')  // ["立春", "雨水", "啓蟄", ..., "小寒", "大寒"]

11.3 查询某日是否为节气

const date = ChineseDate.fromGregorian(2024, 4, 4)
date.getSolarTermName('zh-CN')  // "清明"

const date2 = ChineseDate.fromGregorian(2024, 6, 21)
date2.getSolarTermName('zh-CN')  // null — 这天不是节气

11.4 全年节气

const terms = ChineseDate.solarTermsInYear(2024, 'zh-CN')
// [
//   { index: 1, name: "立春", date: { year: 2024, month: 2, day: 4 }, jde: 2460344.2... },
//   { index: 2, name: "雨水", date: { year: 2024, month: 2, day: 19 }, jde: 2460359.2... },
//   ...
//   { index: 24, name: "大寒", date: { year: 2025, month: 1, day: 20 }, jde: 2460694.2... }
// ]

12. 传统节日

12.1 获取节日日期

ChineseDate.festivalDate('spring', 2024)       // { year: 2024, month: 2, day: 10 }
ChineseDate.festivalDate('mid-autumn', 2024)   // { year: 2024, month: 9, day: 17 }
ChineseDate.festivalDate('dragon-boat', 2024)  // { year: 2024, month: 6, day: 10 }
ChineseDate.festivalDate('new-year-eve', 2024) // { year: 2025, month: 1, day: 28 }

12.2 查询某日的节日

const date = ChineseDate.fromGregorian(2024, 2, 10)

date.getFestivals('zh-CN')  // [{ name: "春节", key: "spring", month: 1, day: 1, leap: false }]
date.getFestivals('zh-HK')  // [{ name: "農曆新年", key: "spring", month: 1, day: 1, leap: false }]
date.festivals               // 同 getFestivals('zh-CN')

12.3 全年节日

ChineseDate.festivalsInYear(2024, 'zh-CN')
// [
//   { name: "春节", key: "spring", date: { year: 2024, month: 2, day: 10 }, ... },
//   { name: "元宵节", key: "lantern", date: { year: 2024, month: 2, day: 24 }, ... },
//   ...
// ]

ChineseDate.festivalsInYear(2024, 'zh-HK')
// [
//   { name: "農曆新年", key: "spring", date: { year: 2024, month: 2, day: 10 }, ... },
//   ...
// ]

12.4 内置节日一览

Key 农历日期 zh-CN zh-TW zh-HK
spring 正月初一 春节 春節 農曆新年
lantern 正月十五 元宵节 元宵節 元宵節
dragon-head 二月初二 龙抬头 龍抬頭 龍抬頭
shangsi 三月初三 上巳节 上巳節 上巳節
dragon-boat 五月初五 端午节 端午節 端午節
qixi 七月初七 七夕节 七夕 七夕
ghost 七月十五 中元节 中元節 中元節
mid-autumn 八月十五 中秋节 中秋節 中秋節
double-ninth 九月初九 重阳节 重陽節 重陽節
laba 腊月初八 腊八节 臘八節 臘八節
little-new-year-north 腊月廿三 小年(北方)
little-new-year-south 腊月廿四 小年(南方) 小年 小年
new-year-eve 腊月三十 除夕 除夕 除夕

"—" 表示该地区不庆祝此节日,festivalsInYear 会自动排除。


13. 多历法

13.1 四种历法变体

import { ChineseDate, JapaneseDate, KoreanDate, VietnameseDate } from 'chinese-lunar-date'

// 中国农历 (UTC+8)
const cn = ChineseDate.fromGregorian(2024, 6, 21)

// 日本旧历 (UTC+9)
const jp = JapaneseDate.fromGregorian(2024, 6, 21)

// 韩国农历 (UTC+8:30/9,檀君纪元)
const kr = KoreanDate.fromGregorian(2024, 6, 21)

// 越南农历 (UTC+7/8)
const vn = VietnameseDate.fromGregorian(2024, 6, 21)

所有子类都支持与 ChineseDate 相同的构造方法和实例方法:

// 构造方法
JapaneseDate.fromGregorian(2024, 6, 21)
JapaneseDate.fromDate(new Date())
JapaneseDate.fromValue({ cycle: 78, year: 34, month: 5, leap: false, day: 6 })
JapaneseDate.fromISO('2024-06-21')
JapaneseDate.fromTimestamp(Date.now())
JapaneseDate.today()

// 实例方法
jp.yearGanZhi       // "甲辰"
jp.formatFull()     // "甲辰年 五月初六"
jp.toGregorian()    // { year: 2024, month: 6, day: 21 }

13.2 时区差异的影响

由于时区不同,同一公历日期在不同历法中可能对应不同的农历日期:

// 假设新月发生在 UTC+8 的午夜附近
const cn = ChineseDate.fromGregorian(2024, 1, 12)   // 可能是腊月初二
const jp = JapaneseDate.fromGregorian(2024, 1, 12)  // 可能是腊月初三(UTC+9 已过午夜)

13.3 韩国历法的特殊纪元

韩国历法使用檀君纪元(公元前 2333 年),因此 cycleyear 与中国历法不同:

const cn = ChineseDate.fromGregorian(2024, 6, 21)
cn.cycle  // 78
cn.year   // 34

const kr = KoreanDate.fromGregorian(2024, 6, 21)
kr.cycle  // 83(檀君纪元更早,周期序号更大)
kr.year   // 不同值

// 但公历年相同
cn.gregorianYear  // 2024
kr.gregorianYear  // 2024

14. 多语言 / 多地区

14.1 支持的语言

import { availableLocales, isLocaleSupported } from 'chinese-lunar-date'

availableLocales()  // ["zh-CN", "zh-TW", "zh-HK", "ja-JP", "ko-KR", "vi-VN"]
isLocaleSupported('zh-CN')  // true
isLocaleSupported('zh-MO')  // false

14.2 获取地区数据

import { getLocale } from 'chinese-lunar-date'

const loc = getLocale('ja-JP')
loc.gan           // ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"]
loc.zhi           // ["子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"]
loc.zodiac        // ["鼠", "牛", "虎", "兔", "竜", "蛇", "馬", "羊", "猿", "鶏", "犬", "猪"]
loc.monthNames(12, false)  // "師走"
loc.monthNames(6, false)   // "水無月"
loc.dayNames(15)           // "十五"
loc.solarTerms             // ["立春", "雨水", "啓蟄", ...]
loc.yearFormat('甲辰', '辰')  // "甲辰辰年"

14.3 繁体中文地区差异

项目 zh-CN zh-TW zh-HK
生肖 龙 马 鸡 猪 龍 馬 雞 豬 龍 馬 雞 豬
闰月标记
腊月 腊月 臘月 臘月
春节名称 春节 春節 農曆新年
节气用字 惊蛰 谷雨 处暑 小满 芒种 驚蟄 穀雨 處暑 小滿 芒種 驚蟄 穀雨 處暑 小滿 芒種
小年 分南北方 统一(廿四) 统一(廿四)

15. 低级 API

如需更细粒度的控制,可直接使用核心算法函数。这些函数是纯函数,无副作用。

15.1 核心转换函数

import {
  gregorianToLunar, lunarToGregorian, lunarToJDE, fromJDE,
  newYear, yearFromEpochCycle,
  inMajorSolarTerm, isLeapMonth,
  CHINESE_CONFIG, JAPANESE_CONFIG, KOREAN_CONFIG, VIETNAMESE_CONFIG
} from 'chinese-lunar-date'

// 公历 → 农历
const lunar = gregorianToLunar(CHINESE_CONFIG, 2024, 6, 21)
// { cycle: 78, year: 34, month: 5, leap: false, day: 6 }

// 农历 → 公历
const greg = lunarToGregorian(CHINESE_CONFIG, lunar)
// { year: 2024, month: 6, day: 21 }

// 农历 → JDE
const jde = lunarToJDE(CHINESE_CONFIG, lunar)

// JDE → 农历
const lunar2 = fromJDE(CHINESE_CONFIG, jde)

// 春节 JDE
const nyJDE = newYear(CHINESE_CONFIG, 2024)

// 周期/年序 → 公历年
yearFromEpochCycle(CHINESE_CONFIG, 78, 34)  // 2024

// 判断是否中气月
inMajorSolarTerm(CHINESE_CONFIG, lunar)  // true/false

// 判断是否闰月
isLeapMonth(CHINESE_CONFIG, 2024, 5)  // false(2024年五月不是闰月)

15.2 天文计算函数

import {
  solarTermJDE, majorSolarTermJDE, minorSolarTermJDE,
  nextNewMoon, previousNewMoon, midnight
} from 'chinese-lunar-date'

// 节气 JDE
solarTermJDE(CHINESE_CONFIG, 5, 2024)    // 清明的 JDE
majorSolarTermJDE(CHINESE_CONFIG, 3, 2024)  // 第3个中气的 JDE
minorSolarTermJDE(CHINESE_CONFIG, 3, 2024)  // 第3个节气的 JDE

// 新月 JDE
nextNewMoon(2460482)      // 下一个新月的 JDE
previousNewMoon(2460482)  // 上一个新月的 JDE

// 午夜 JDE
midnight(2024, 6, 21)  // 2024-06-21 午夜的 JDE

15.3 历法配置对象

import { CHINESE_CONFIG, JAPANESE_CONFIG, KOREAN_CONFIG, VIETNAMESE_CONFIG, getConfig } from 'chinese-lunar-date'

// 直接使用预定义配置
CHINESE_CONFIG.type       // 'chinese'
CHINESE_CONFIG.epochYear  // -2636

// 通过类型获取配置
getConfig('korean')  // KOREAN_CONFIG

16. 中文数字

import { toChineseYear, toChineseNumber, getWeekdayName, getWeekdayShort } from 'chinese-lunar-date'

// 年份转换
toChineseYear(2025, 'upper')  // "二〇二五"(〇版,日期常用)
toChineseYear(2025, 'lower')  // "二零二五"(零版,口语常用)
toChineseYear(1900, 'upper')  // "一九〇〇"

// 数字转换(1-99)
toChineseNumber(1, 'upper')   // "一"
toChineseNumber(10, 'upper')  // "十"
toChineseNumber(11, 'upper')  // "十一"
toChineseNumber(21, 'upper')  // "二十一"
toChineseNumber(30, 'upper')  // "三十"
toChineseNumber(99, 'upper')  // "九十九"

// 星期名称
getWeekdayName(0, 'zh-CN')   // "星期日"
getWeekdayName(6, 'zh-CN')   // "星期六"
getWeekdayName(6, 'ja-JP')   // "土曜日"
getWeekdayName(6, 'ko-KR')   // "토요일"
getWeekdayName(6, 'vi-VN')   // "Thứ Bảy"

// 星期简称
getWeekdayShort(6, 'zh-CN')  // "六"
getWeekdayShort(6, 'ja-JP')  // "土"
getWeekdayShort(6, 'ko-KR')  // "토"
getWeekdayShort(6, 'vi-VN')  // "T7"

17. 类型参考

/** 农历日期值 */
interface LunarDateValue {
  cycle: number   // 六十甲子周期序号(1 起始)
  year: number    // 周期内年序(1-60)
  month: number   // 月(1-12)
  leap: boolean   // 是否闰月
  day: number     // 日(1-30)
}

/** 公历日期值 */
interface GregorianDate {
  year: number
  month: number
  day: number
}

/** 历法类型 */
type CalendarType = 'chinese' | 'korean' | 'japanese' | 'vietnamese'

/** 历法配置 */
interface CalendarConfig {
  type: CalendarType
  epochYear: number
  epochJDE: number
  timeshiftUTC(year: number, month: number, day: number): number
}

/** 干支索引对 */
interface GanZhiPair {
  gan: number   // 天干索引(0-9)
  zhi: number   // 地支索引(0-11)
}

/** 年月日干支完整信息 */
interface GanZhiInfo {
  year: GanZhiPair
  month: GanZhiPair
  day: GanZhiPair
}

/** 节气信息 */
interface SolarTermInfo {
  index: number           // 节气序号(1-24)
  name: string            // 节气名称(本地化)
  date: GregorianDate     // 公历日期
  jde: number             // 儒略日
}

/** 节日信息 */
interface FestivalInfo {
  name: string            // 节日名称(本地化)
  key: string             // 节日标识
  month: number           // 农历月
  day: number             // 农历日
  leap: boolean           // 是否闰月
}

/** 语言/地区代码 */
type LocaleCode = 'zh-CN' | 'zh-TW' | 'zh-HK' | 'ja-JP' | 'ko-KR' | 'vi-VN'

/** 地区数据 */
interface LocaleData {
  code: LocaleCode
  gan: string[]
  zhi: string[]
  zodiac: string[]
  solarTerms: string[]
  monthNames(month: number, leap: boolean): string
  dayNames(day: number): string
  yearFormat(ganzhi: string, zodiac: string): string
}

/** 格式化 Token 类型 */
type FormatToken =
  | 'CY' | 'Y' | 'Ygz' | 'Z' | 'M' | 'Mname' | 'L' | 'D' | 'Dname'
  | 'GY' | 'GYcn' | 'GYcn2' | 'GM' | 'GM0' | 'GMcn' | 'GD' | 'GD0' | 'GDcn'
  | 'W' | 'Wname' | 'Wshort'

18. 常见问题(FAQ)

Q: `cycle` 和 `year` 有什么区别?为什么不直接用一个年份数字?

中国农历使用六十甲子周期系统。cycle 标识第几个 60 年周期,year 标识周期内的第几年(1-60)。这种设计:

Q: 为什么同一公历日期,`ChineseDate` 和 `JapaneseDate` 的农历日期不同?

这是因为时区差异。当新月发生在 UTC+8 的午夜附近时,日本(UTC+9)可能已经进入下一天,导致农历月份或日期不同。

Q: 韩国农历的 `cycle` 为什么和中国不同?

韩国历法使用檀君纪元(公元前 2333 年),比中国纪元(公元前 2636 年)晚 297 年,因此周期序号不同。但 gregorianYear 属性返回的公历年份是相同的。

Q: 如何判断某年是否有闰月?是哪个闰月?

// 方法1:通过低级 API
import { isLeapMonth, CHINESE_CONFIG } from 'chinese-lunar-date'

// 检查 2023 年八月是否为闰月
isLeapMonth(CHINESE_CONFIG, 2023, 8)  // false

// 方法2:通过值对象遍历
// 创建该年任意一天的实例,然后遍历月份

Q: 如何获取某月的天数?

const date = ChineseDate.fromGregorian(2024, 6, 21)
date.daysInMonth()  // 29 或 30

Q: `format()` 方法中公历相关 Token 为什么显示为空?

使用值对象的 format() 方法时,公历数据会自动提供。但如果使用独立的 format() 函数,需要手动传入公历日期:

// 值对象方法 — 自动提供公历数据
date.format('{GY}年{GM}月{GD}日')  // "2024年6月21日" ✓

// 独立函数 — 需要手动传入公历日期
format(value, jde, greg, '{GY}年{GM}月{GD}日')  // ✓
format(value, jde, '{GY}年{GM}月{GD}日')         // "" — 公历 Token 为空 ✗

Q: IIFE 格式如何使用?

<script src="dist/chinese-lunar-date.iife.js"></script>
<script>
  // 类名需要写两遍:ChineseDate.ChineseDate
  const date = ChineseDate.ChineseDate.fromGregorian(2024, 6, 21)

  // 函数直接在 ChineseDate 对象上
  const lunar = ChineseDate.gregorianToLunar(ChineseDate.CHINESE_CONFIG, 2024, 6, 21)
</script>

Q: 精度如何?

本库基于 VSOP87 行星理论的天文算法,与天文台发布的历表精度一致(误差在秒级)。自研轻量天文模块通过截断 VSOP87 数据(阈值 1e-7,保留 220/2564 项),在保持足够精度的同时大幅减小体积。


反馈与协作

如果您在使用过程中遇到问题,或有功能建议、商务合作等需求,欢迎通过邮件联系我们:

📧 gyfinjava@163.com


19. 架构与设计原则

src/
├── types.ts                # 核心类型定义
├── core/
│   ├── astronomy-lite.ts   # 自研轻量天文模块(VSOP87 截断数据)
│   ├── vsop87Bearth-truncated.ts  # 截断版 VSOP87 地球数据
│   ├── config.ts           # 四种历法配置(纪元 + 时区历史)
│   └── algorithm.ts        # 核心算法(公历 ↔ 农历转换)
├── ganzhi.ts               # 天干地支
├── zodiac.ts               # 生肖
├── solar-term.ts           # 24 节气
├── festival.ts             # 传统节日
├── locale.ts               # 多语言 / 多地区
├── format.ts               # 格式化 + 解析
├── chinese-numerals.ts     # 中文数字
├── lunar-date.ts           # 不可变值对象 API
└── index.ts                # 主入口

设计原则:


20. 更新日志

1.0.0