Documentation
¶
Overview ¶
Package amlich computes the Vietnamese and Korean lunisolar calendars from first-principles astronomy — new-moon instants and apparent solar longitude — instead of lookup tables, so any date (past or future) can be converted.
The same astronomical month runs on different civil clocks: Vietnam numbers its calendar at UTC+7, Korea at UTC+9 (China at UTC+8). When a new moon falls close to local midnight, the two national calendars can start a month on different days — which is why Tết and Seollal, or Trung thu and Chuseok, occasionally land on different dates. Divergence enumerates those years.
Accuracy: the truncated series used here (Meeus, via the reference implementation by Hồ Ngọc Đức) is reliable for roughly 1200–3000 CE; results are cross-validated against the production implementation inside TEdu for 1900–2199 (see testdata/).
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrInvalidLunarDate = errors.New("amlich: lunar date does not exist")
ErrInvalidLunarDate is returned when a lunar date does not exist, e.g. day 30 of a 29-day month, or a leap-month flag on a year/month without one.
var ErrInvalidSolarDate = errors.New("amlich: invalid solar date")
ErrInvalidSolarDate is returned for an impossible civil date.
Functions ¶
func LunarToSolar ¶
LunarToSolar converts a lunisolar date (as numbered in zone z) to the civil (Gregorian) date it falls on. Unlike the reference implementation, it validates month length: day 30 of a 29-day month is ErrInvalidLunarDate instead of silently overflowing into the next month.
Example ¶
package main
import (
"fmt"
"github.com/doxuta/amlich"
)
func main() {
// Trung thu (full moon of the 8th month), lunar year 2026.
y, m, d, _ := amlich.LunarToSolar(amlich.LunarDate{Year: 2026, Month: 8, Day: 15}, amlich.Vietnam)
fmt.Printf("%04d-%02d-%02d\n", y, m, d)
}
Output: 2026-09-25
Types ¶
type Country ¶
type Country int
Country selects a national lunisolar tradition: which holidays are observed and which civil clock (Zone) numbers the calendar.
type Divergent ¶
type Divergent struct {
LunarDay int
LunarMonth int
LunarYear int
NameVN string
NameKR string
VN string // YYYY-MM-DD as observed in Vietnam
KR string // YYYY-MM-DD as observed in Korea
}
Divergent is a lunar anchor date whose civil date differs between the Vietnamese (UTC+7) and Korean (UTC+9) calendars in a given year.
func Divergence ¶
Divergence enumerates, over lunar years [fromYear, toYear], the shared VN/KR observances whose civil dates differ because the same new moon falls on different sides of midnight at UTC+7 versus UTC+9.
type Holiday ¶
type Holiday struct {
Name string // native name
NameEN string
Day int // lunar day
Month int // lunar month
Country Country
}
Holiday is a lunisolar holiday definition.
type HolidayDate ¶
type HolidayDate struct {
Holiday
Year int // Gregorian year the holiday falls in
SolarY int
SolarM int
SolarD int
}
HolidayDate is a holiday resolved to a civil date for a specific year.
func Holidays ¶
func Holidays(year int, c Country) []HolidayDate
Holidays resolves the country's lunisolar holidays that fall inside Gregorian year year, in calendar order.
Lunar months 1..8 of lunar year Y fall in Gregorian year Y; month 12 dates (Ông Táo) fall early in Gregorian year Y+1, so they are resolved from lunar year year-1.
Example ¶
package main
import (
"fmt"
"github.com/doxuta/amlich"
)
func main() {
for _, h := range amlich.Holidays(2026, amlich.KR)[:2] {
fmt.Println(h.ISO(), h.NameEN)
}
}
Output: 2026-02-17 Seollal (Lunar New Year) 2026-03-03 Daeboreum (First Full Moon)
func (HolidayDate) ISO ¶
func (h HolidayDate) ISO() string
ISO renders the resolved civil date as YYYY-MM-DD.
type LunarDate ¶
LunarDate is a date in a lunisolar calendar. Leap reports whether the date belongs to the intercalary (leap) month with the same Month number.
func SolarToLunar ¶
SolarToLunar converts a civil (Gregorian) date to the lunisolar date as numbered in zone z.
Example ¶
package main
import (
"fmt"
"github.com/doxuta/amlich"
)
func main() {
l, _ := amlich.SolarToLunar(2026, 2, 17, amlich.Vietnam)
fmt.Println(l, amlich.CanChi(l.Year).Vietnamese)
}
Output: 1/1/2026 Bính Ngọ
type YearName ¶
type YearName struct {
Vietnamese string // e.g. "Bính Ngọ"
Korean string // e.g. "병오년"
AnimalEN string // e.g. "Horse" — note: branch 4 (Mão) is Cat in Vietnam, Rabbit elsewhere
}
YearName is the sexagenary name of a lunar year in both readings.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
amlich
command
Command amlich is a small CLI for the amlich lunisolar engine.
|
Command amlich is a small CLI for the amlich lunisolar engine. |
|
amlich-mcp
command
Command amlich-mcp exposes the amlich lunisolar engine as an MCP server (stdio transport), so AI agents can compute — not guess — Vietnamese and Korean lunar dates and holidays.
|
Command amlich-mcp exposes the amlich lunisolar engine as an MCP server (stdio transport), so AI agents can compute — not guess — Vietnamese and Korean lunar dates and holidays. |