when-am-i / commit
Add Vietnamese calendar (lunisolar pre-1954, Gregorian since 1954, Tết today)
README.md | 40 +++++---- .../vietnam/01-vietnamese-lunisolar-calendar.png | Bin 0 -> 683649 bytes screenshots/calendars/vietnam/02-gregorian.png | Bin 0 -> 663628 bytes .../vietnam/03-vietnamese-lunisolar-calendar.png | Bin 0 -> 663628 bytes src/data/calendars/vietnam.json | 93 +++++++++++++++++++++ src/utils/calendars.test.ts | 8 ++ src/utils/calendars.ts | 16 ++++ 7 files changed, 136 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index f7aaf2d..96800be 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [](https://gitlab.com/PavelSafronov/when-am-i/-/pipelines) [](https://pavelsafronov.gitlab.io/when-am-i)  - +   @@ -61,11 +61,12 @@ and show the Gregorian year for reference). sourced table of each year's actual equinox-based start date), Dangi (Dangun era, South Korea), Juche (North Korea, 1997–2024). - **Descriptive-only:** the Chinese sexagenary cycle (the named 60-year - stem-branch / zodiac-animal year), the Korean and Japanese pre-modern - lunisolar calendars, the Tzolk'in/Haab' Calendar Round, the Aztec/Mexica - year-bearer (52-year cycle) and Tōnalpōhualli (260-day ritual cycle), the - Ottoman Rumi fiscal calendar, the Māori maramataka, and observational lunar - calendars such as the Algonquian (Massachusett) reckoning. + stem-branch / zodiac-animal year), the Korean, Vietnamese, and Japanese + pre-modern lunisolar calendars, the Tzolk'in/Haab' Calendar Round, the + Aztec/Mexica year-bearer (52-year cycle) and Tōnalpōhualli (260-day ritual + cycle), the Ottoman Rumi fiscal calendar, the Māori maramataka, and + observational lunar calendars such as the Algonquian (Massachusett) + reckoning. ## Tech Stack @@ -253,39 +254,36 @@ see [How to contribute](#how-to-contribute) above, and check `src/utils/calendars.ts` for whether a system needs new converter work or can ship as descriptive-only. -1. **Vietnamese calendar** — a Chinese-derived lunisolar calendar (own - sexagenary-cycle and era-name conventions) used for ~1,000+ years across - Vietnam; still observed for Tết and traditional dates today. -2. **Tibetan calendar (Bod rtsis)** — a lunisolar calendar in continuous use +1. **Tibetan calendar (Bod rtsis)** — a lunisolar calendar in continuous use across the Tibetan Plateau for over a thousand years, with its own 60-year cycle and year names. -3. **Mongolian calendar** — the Mongol Empire and successor states' lunisolar +2. **Mongolian calendar** — the Mongol Empire and successor states' lunisolar calendar (related to but distinct from the Chinese and Tibetan systems), spanning a vast historical territory. -4. **Coptic calendar** — Egypt's calendar since antiquity (descended from the +3. **Coptic calendar** — Egypt's calendar since antiquity (descended from the ancient Egyptian civil calendar), still used liturgically by the Coptic Orthodox Church; distinct from but related to the Ethiopian calendar already supported. (Note: the Temporal `coptic` calendar id is broken in our polyfill version, same class of bug as `chinese`/`ethiopic` — would likely need a hand-rolled converter like Ethiopian's.) -5. **Ancient Egyptian civil calendar** — a 365-day calendar used for roughly +4. **Ancient Egyptian civil calendar** — a 365-day calendar used for roughly three millennia along the Nile; descriptive-only (no continuous numbered year survives in common use), but enormous time depth. -6. **Armenian calendar** — in continuous use since antiquity, with its own +5. **Armenian calendar** — in continuous use since antiquity, with its own epoch and a fixed 365-day year (no leap day), giving it a slow drift against the Gregorian year worth surfacing. -7. **Coast Salish / Puget Sound calendar** — a sourced, people-specific lunar +6. **Coast Salish / Puget Sound calendar** — a sourced, people-specific lunar reckoning for the Pacific Northwest, paralleling the Massachusett entry; the place/people this skill's original brainstorm named as an example. -8. **Inca calendar** — a lunisolar/solar calendar used across the Inca Empire's +7. **Inca calendar** — a lunisolar/solar calendar used across the Inca Empire's considerable territory in the Andes; mostly descriptive (reconstructions of its exact rules are debated, so sourcing needs care). -9. **Balinese Pawukon and Saka calendars** — Bali's parallel 210-day ritual +8. **Balinese Pawukon and Saka calendars** — Bali's parallel 210-day ritual cycle and a Saka-derived solar count, both still in everyday use. -10. **Bahá'í calendar** — a 19-month, 19-day solar calendar in worldwide use - since the mid-19th century; smaller area than the others here but a long - enough span and clean enough arithmetic to be a good convertible candidate. -11. **Sikh Nanakshahi calendar** — a solar calendar adopted by the SGPC in 2003 +9. **Bahá'í calendar** — a 19-month, 19-day solar calendar in worldwide use + since the mid-19th century; smaller area than the others here but a long + enough span and clean enough arithmetic to be a good convertible candidate. +10. **Sikh Nanakshahi calendar** — a solar calendar adopted by the SGPC in 2003 for Sikh religious dates, with a clear epoch (Guru Nanak's birth year) — likely a simple convertible offset like Dangi/Juche. diff --git a/screenshots/calendars/vietnam/01-vietnamese-lunisolar-calendar.png b/screenshots/calendars/vietnam/01-vietnamese-lunisolar-calendar.png new file mode 100644 index 0000000..63614ef Binary files /dev/null and b/screenshots/calendars/vietnam/01-vietnamese-lunisolar-calendar.png differ diff --git a/screenshots/calendars/vietnam/02-gregorian.png b/screenshots/calendars/vietnam/02-gregorian.png new file mode 100644 index 0000000..98f8dfe Binary files /dev/null and b/screenshots/calendars/vietnam/02-gregorian.png differ diff --git a/screenshots/calendars/vietnam/03-vietnamese-lunisolar-calendar.png b/screenshots/calendars/vietnam/03-vietnamese-lunisolar-calendar.png new file mode 100644 index 0000000..98f8dfe Binary files /dev/null and b/screenshots/calendars/vietnam/03-vietnamese-lunisolar-calendar.png differ diff --git a/src/data/calendars/vietnam.json b/src/data/calendars/vietnam.json new file mode 100644 index 0000000..734152e --- /dev/null +++ b/src/data/calendars/vietnam.json @@ -0,0 +1,93 @@ +{ + "region": { + "name": "Vietnam", + "geo": { + "type": "Polygon", + "coordinates": [ + [ + [108.1, 21.6], + [106.7, 20.7], + [105.9, 19.8], + [105.7, 19.1], + [106.4, 18.0], + [107.4, 16.7], + [108.3, 16.1], + [108.9, 15.3], + [109.3, 13.4], + [109.2, 11.7], + [108.4, 11.0], + [107.2, 10.4], + [106.4, 9.5], + [105.2, 8.6], + [104.8, 9.2], + [105.1, 9.9], + [104.3, 10.5], + [105.2, 10.9], + [106.2, 11.0], + [105.8, 11.6], + [107.5, 12.3], + [107.6, 13.5], + [107.4, 14.2], + [107.6, 15.2], + [107.3, 15.9], + [106.6, 16.6], + [105.9, 17.5], + [105.1, 18.7], + [103.9, 19.3], + [104.2, 19.6], + [104.8, 19.9], + [104.4, 20.8], + [103.2, 20.8], + [102.8, 21.7], + [102.2, 22.5], + [102.7, 22.7], + [103.5, 22.7], + [104.5, 22.8], + [105.3, 23.4], + [105.8, 23.0], + [106.7, 22.8], + [106.6, 22.2], + [107.0, 21.8], + [108.1, 21.6] + ] + ] + } + }, + "eras": [ + { + "name": "Traditional lunisolar calendar (pre-1954)", + "start": null, + "end": "1954-01-01", + "calendar": "Vietnamese lunisolar calendar", + "usedBy": "the Vietnamese", + "description": "For most of recorded history Vietnam used a lunisolar calendar derived from Chinese models, recalculated by court astronomers under successive dynasties (recorded calendar names include Thụ Thời/Hiệp Kỷ from the Trần dynasty in the 1320s, Khâm Thụ under the Lê dynasty from 1413, and a Qing-derived Hiệp Kỷ method under the Nguyễn dynasty from 1813). Years were not numbered continuously; they were cited by the 60-year sexagenary cycle (can chi) or by the reigning emperor's era name and regnal year (e.g. \"Thành Thái canh dần niên\" for 1890), the same conventions used in China and Korea. The Gregorian calendar became Vietnam's official civil calendar in 1954.", + "sources": [ + "https://en.wikipedia.org/wiki/Vietnamese_calendar", + "https://en.wikipedia.org/wiki/Tr%E1%BA%A7n_dynasty", + "https://en.wikipedia.org/wiki/Nguy%E1%BB%85n_dynasty" + ] + }, + { + "name": "Gregorian calendar (official)", + "start": "1954-01-01", + "end": null, + "calendar": "Gregorian", + "usedBy": "the Vietnamese government", + "description": "The Gregorian calendar has been Vietnam's official calendar since 1954. North Vietnam made it the sole official calendar (restricting the lunisolar calendar to holidays and commemorations) by an 8 August 1967 decree that also shifted the country's standard time from UTC+8 to UTC+7; South Vietnam adopted the same restriction at the end of the Vietnam War in 1975.", + "sources": [ + "https://en.wikipedia.org/wiki/Vietnamese_calendar" + ] + }, + { + "name": "Lunisolar calendar (cultural use, modern)", + "start": "1954-01-01", + "end": null, + "calendar": "Vietnamese lunisolar calendar", + "usedBy": "the Vietnamese", + "description": "Although no longer the official civil calendar after 1954, the lunisolar calendar remains in everyday cultural use for holidays such as Tết Nguyên Đán (Lunar New Year) and Tết Trung Thu (Mid-Autumn Festival). Since Vietnam's 1967/1975 shift to UTC+7 for its calendar calculations, it can diverge by up to a month from the Chinese calendar (which uses UTC+8): in 1985, for example, Vietnamese New Year fell on 21 January while Chinese New Year fell on 20 February, a gap resolved only when Vietnam inserted its own leap month from 21 March to 19 April that year.", + "sources": [ + "https://en.wikipedia.org/wiki/Vietnamese_calendar" + ] + } + ] +} diff --git a/src/utils/calendars.test.ts b/src/utils/calendars.test.ts index ed35217..5a39cca 100644 --- a/src/utils/calendars.test.ts +++ b/src/utils/calendars.test.ts @@ -170,6 +170,14 @@ describe('convert', () => { expect(c.localYearLabel).toBe('1800'); // Gregorian reference expect(c.localDateLabel).toBeTruthy(); // computed moon phase }); + + it('Vietnamese lunisolar calendar is descriptive-only but reports the moon', () => { + const c = convert({ year: 1929, month: 7, day: 1 }, 'Vietnamese lunisolar calendar'); + expect(c.convertible).toBe(false); + expect(c.system).toBe('Vietnamese lunisolar calendar'); + expect(c.localYearLabel).toBe('1929'); // Gregorian reference + expect(c.localDateLabel).toBeTruthy(); // computed moon phase + }); }); describe('wintersSince', () => { diff --git a/src/utils/calendars.ts b/src/utils/calendars.ts index 1ea3201..1027811 100644 --- a/src/utils/calendars.ts +++ b/src/utils/calendars.ts @@ -430,6 +430,22 @@ CONVERTERS['korean lunisolar calendar'] = (date) => ({ note: 'This calendar reckons months by the moon (with an intercalary month some years), not a continuous numbered year; traditional Korean records instead cited Chinese imperial era names or the 60-year sexagenary cycle.', }); +// Vietnam's traditional civil calendar (pre-1954) and continuing cultural +// calendar (Tết Nguyên Đán, Tết Trung Thu): a Chinese-derived lunisolar +// calendar, but computed independently rather than borrowed wholesale — since +// Vietnam's 1967/1975 shift to UTC+7 (versus China's UTC+8) it can place its +// new year and intercalary months up to a month away from the Chinese +// calendar. No continuous numbered year of its own; years were historically +// cited by the sexagenary cycle or imperial era names. Descriptive-only, like +// the Korean and Chinese lunisolar entries above. +CONVERTERS['vietnamese lunisolar calendar'] = (date) => ({ + system: 'Vietnamese lunisolar calendar', + localYearLabel: yearLabel(date.year), // Gregorian reference year + localDateLabel: lunarPhaseLabel(date), + convertible: false, + note: 'This calendar reckons months by the moon (with an intercalary month some years), not a continuous numbered year; traditional Vietnamese records instead cited the 60-year sexagenary cycle or the reigning emperor’s era name. Computed independently of the Chinese calendar (using Vietnam’s own time zone), it can place its new year and leap months up to a month apart from China’s.', +}); + /** Convert `date` into the named calendar's local year. */ export function convert(date: HistoricalDate, calendarName: string): CalendarConversion { const converter = CONVERTERS[calendarName.trim().toLowerCase()];