
Security News
/Company News
Securing the Financial Frontier: How Capital One Uses Socket for Open Source Security
Capital One is partnering with Socket to proactively secure its open source supply chain.
calendaryjs-plugin-lunar
Advanced tools
Lunar (lunisolar) calendar plugin for calendaryjs — solar ↔ lunar date conversion (1900–2100) and lunar recurring events using the standard astronomical lunisolar table.
New to calendaryjs? Start with the core README — this plugin builds on its engine and builder.
Adds the East Asian lunisolar calendar to calendaryjs: declare events by lunar
date, and convert solar ↔ lunar (1900–2100) with leap months handled. Ships two
national variants — Chinese (default, the published almanac table) and
Vietnamese (âm lịch, astronomical at UTC+7) — selected like an
Intl/Temporal calendar id.
npm i calendaryjs calendaryjs-plugin-lunar
Register the plugin, then declare lunar events with the lunar.date() selector — the
engine converts them to solar dates automatically:
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"),
],
});
cal.getEventsInRange("2025-01-01", "2025-12-31"); // → solar dates
Prefer raw config? The selector compiles to a plain lunar event:
{ type: "lunar", id: "tet", lunarMonth: 1, lunarDay: 1, title: "Lunar New Year" }
The Chinese calendar is computed at 120°E (UTC+8), the Vietnamese one at 105°E (UTC+7). They are distinct national standards, not timezones: whenever a new moon falls between 23:00 and 24:00 Vietnam time, the Vietnamese month starts a day earlier — occasionally moving Tết (1968, 1985, 2007, 2030) or a leap-month boundary (1984, 1985, 1987, 1995, 2031).
The default is "chinese" (the standard almanac table). Opt into Vietnamese
âm lịch per plugin instance — it applies to events and day enrichment alike:
const cal = calendary().use(lunar({ calendar: "vietnamese" }));
// 2019-03-17 is 12/2 âm lịch (Vietnam) but 11/2 in the Chinese calendar:
solarToLunar({ year: 2019, month: 3, day: 17 }, { calendar: "vietnamese" });
// → { year: 2019, month: 2, day: 12, isLeapMonth: false }
solarToLunar({ year: 2019, month: 3, day: 17 });
// → { year: 2019, month: 2, day: 11, isLeapMonth: false }
The Vietnamese variant uses the Ho Ngoc Duc astronomical algorithm — the de-facto Vietnamese standard. The Chinese variant keeps the published table (deliberately not "the same algorithm at UTC+8", which would drift from the official Chinese calendar on 155 days in 1900–2100).
Sharing events? Pin the calendar on the event. The instance option is your
app's default; a stored/shared event can carry its own calendar field, which
wins over the instance default — so a .cdy collection is self-describing
and renders the same dates for every consumer:
// Builder: pin per event
every("year")
.on(lunar.date(2, 12, { calendar: "vietnamese" }))
.title("Giỗ bà");
// .cdy — self-describing, regardless of how the consumer registers lunar()
{
"plugins": ["calendaryjs-plugin-lunar"],
"events": [
{
"type": "lunar",
"id": "gio-ba",
"title": "Giỗ bà",
"lunarMonth": 2,
"lunarDay": 12,
"calendar": "vietnamese"
}
]
}
Every conversion takes an optional { calendar } as the last argument
(default "chinese"):
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 }, { calendar: "vietnamese" });
// → { year: 2025, month: 1, day: 1, isLeapMonth: false }
In a leap year a lunar month repeats (2023 has a leap 2nd month — 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");
Working from a known solar date (a death date for a giỗ, a birthday)? Let
lunar.fromSolar compute the flag instead of guessing it:
lunar.fromSolar(new Date(2023, 3, 5)); // Apr 5, 2023
// → { lunarMonth: 2, lunarDay: 15, isLeapMonth: true }
Event type lunar — lunarMonth (1–12) · lunarDay (1–30) · isLeapMonth?
(prefer the leap month; see above) · calendar? (pin the variant per event —
wins over the instance default), plus every standard
event property.
Plugin options — enrichDays?: boolean · calendar?: "chinese" | "vietnamese"
(default "chinese").
Exports — lunar() · lunar.date(month, day, { leap?, calendar? }) ·
lunar.fromSolar(date, { calendar? }) · lunarToSolar · solarToLunar ·
isValidLunarDate (each takes an optional { calendar }).
MIT. This plugin requires the calendaryjs core, which is licensed
PolyForm Noncommercial — so commercial use of the combined work requires a
commercial license for the core. See Commercial licensing.
FAQs
Lunar (lunisolar) calendar plugin for calendaryjs — solar ↔ lunar date conversion (1900–2100) and lunar recurring events using the standard astronomical lunisolar table.
We found that calendaryjs-plugin-lunar demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
/Company News
Capital One is partnering with Socket to proactively secure its open source supply chain.

Security News
Socket CTO Ahmad Nassri discusses how to keep AI agents from bypassing package blocks, limit credential access, and monitor their actions.

Security News
GPT-6 Astra tried to plant malicious code in simulated open source projects using fake GitHub accounts and deceptive PRs during an assigned CTF challenge.