Skip to content

calendaryjs-plugin-lunar

Solar ↔ lunar date conversion (1900–2100) plus lunar recurring events. Uses the standard astronomical lunisolar table.

Terminal window
npm i calendaryjs calendaryjs-plugin-lunar

The plugin adds a lunar.date(month, day) builder selector:

import { calendary } from "calendaryjs";
import { every } from "calendaryjs/builder";
import { lunar } from "calendaryjs-plugin-lunar";
const cal = calendary().use(lunar());
cal.addGroup({
id: "lunar-holidays",
events: [
every("year").on(lunar.date(1, 1)).title("Lunar New Year"),
every("year").on(lunar.date(8, 15)).title("Mid-Autumn Festival"),
],
});

lunar.date(month, day) takes lunarMonth (1–12) and lunarDay (1–30) and compiles to a plain lunar event ({ type: "lunar", lunarMonth, lunarDay }) — the storage form. Pass { leap: true } to target a leap month.

import { lunarToSolar, solarToLunar } from "calendaryjs-plugin-lunar";
lunarToSolar({ year: 2025, month: 1, day: 1, isLeapMonth: false });
// → { year: 2025, month: 1, day: 29 }
solarToLunar({ year: 2025, month: 1, day: 29 });
// → { year: 2025, month: 1, day: 1, isLeapMonth: false }

In a leap year a lunar month repeats — 2023 has a leap 2nd month, so two “month 2”s. Events resolve to the regular month by default. Pass { leap: true } (raw: isLeapMonth: true) to prefer the leap month; in years without it the event falls back to the regular month, matching Temporal’s monthCode.

every("year").on(lunar.date(2, 15, { leap: true })).title("Leap 2/15");

Have a known solar date (a death date for a giỗ, a birthday)? lunar.fromSolar computes the flag for you:

import { lunar } from "calendaryjs-plugin-lunar";
lunar.fromSolar(new Date(2023, 3, 5)); // Apr 5, 2023
// → { lunarMonth: 2, lunarDay: 15, isLeapMonth: true }