New:Microsoft Teams Notifications Are Now Available in Socket.Learn more →
Get Started

calendaryjs-plugin-lunar

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

calendaryjs-plugin-lunar

Lunar (lunisolar) calendar plugin for calendaryjs — solar ↔ lunar date conversion (1900–2100) and lunar recurring events using the standard astronomical lunisolar table.

latest
Source
npmnpm
Version
0.3.0
Version published
Maintainers
1
Created
Source

calendaryjs

calendaryjs-plugin-lunar

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 Socket Zero dependencies

Install

npm i calendaryjs calendaryjs-plugin-lunar

Use it

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" }

Calendar variants — Chinese vs Vietnamese

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"
    }
  ]
}

Convert dates

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 }

Leap months

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 }

Reference

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 }).

License

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.

Keywords

lunar

FAQs

Package last updated on 03 Jul 2026

Related posts