Skip to content

Add Vietnamese Lunar Calendar (Âm Lịch) support#2374

Open
dsblank wants to merge 11 commits into
gramps-project:masterfrom
dsblank:feature/vietnamese-lunar-only
Open

Add Vietnamese Lunar Calendar (Âm Lịch) support#2374
dsblank wants to merge 11 commits into
gramps-project:masterfrom
dsblank:feature/vietnamese-lunar-only

Conversation

@dsblank

@dsblank dsblank commented Jun 12, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds the Vietnamese Lunar calendar (CAL_VIETNAMESE_LUNAR, constant 8) backed by a precomputed SDN table covering years 400–9999.
  • Implements vietnamese_lunar_sdn() / vietnamese_lunar_ymd() conversion functions and vietnamese_can_chi_year() for the 60-year Can-Chi sexagenary cycle.
  • Registers DateParserVI / DateDisplayVI for the vi / vi_VN locales so users can enter and display dates entirely in Vietnamese.
  • Leap months are encoded as month + 100 (e.g. month 104 = Nhuận Tháng Tư) and always round-trip through ISO format.

Depends on #2369 (Chinese Lunar Calendar support) — the Vietnamese calendar shares the same SDN infrastructure. Once #2369 merges the diff here will trim to Vietnamese-only changes.

What a Vietnamese-locale user can now type

Input Parsed as
Tháng Giêng 1 2024 Vietnamese Lunar month 1, day 1, year 2024
Tháng Chạp 29 2024 month 12 (Tháng Chạp), day 29
Nhuận Tháng Tư 1 2020 leap month 4, day 1, year 2020
khoảng Tháng Năm 2023 about Vietnamese Lunar month 5, 2023
từ … đến … date span
âm lịch selects Vietnamese Lunar calendar

Display formats: ISO (2024-1-1), numeric (Năm 2024 Tháng Giêng Ngày 1), and Can-Chi year (Năm Giáp Thìn Tháng Giêng Ngày 1).

Test plan

  • Run python3 -m unittest gramps.gen.lib.test.vietnamese_calendar_test — SDN round-trips, Tết anchor dates, Can-Chi names.
  • Run python3 -m unittest gramps.gen.datehandler.test.date_vi_test — all 12 month names, leap months, modifiers, calendar keyword, all three display formats.
  • Run full suite: python3 -m unittest discover -p "*_test.py" — no regressions.
  • With LANG=vi_VN.UTF-8, open the date editor and enter a Vietnamese Lunar date using native month names.

🤖 Generated with Claude Code

Date Display: US (English) vs VI (Vietnamese)

Description US (English) VI (Vietnamese)
Year only 1949 1949
Month + Year 2000-03 3/2000
Full date 2000-03-15 15/3/2000
Before before 2000 trước 2000
After after 1949 sau 1949
About about 1850 khoảng 1850
Before (full date) before 1900-06-15 trước 15/6/1900
After (full date) after 1800-01-01 sau 1/1/1800
Estimated estimated 1800 phỏng chừng 1800
Calculated calculated 1776-06 tính toán 6/1776
Est. + Before estimated before 1900 phỏng chừng trước 1900
Calc. + After calculated after 1800 tính toán sau 1800
Span (from/to) 1900 - 1910 từ 1900 đến 1910
Span (full dates) 1905-03-01 - 1912-11-30 từ 1/3/1905 đến 30/11/1912
Range (between/and) 1900 - 1910 giữa 1900 và 1910
Julian 1582-02-10 (Julian) 10/2/1582 (Julian)
Hebrew 5780-01-01 (Hebrew) 1/1/5780 (Do Thái)
Islamic 1400-01 (Islamic) 1/1400 (Hồi giáo)
French Rep. 10-03-01 (French Republican) 1/3/10 (Cộng hòa Pháp)
Persian 1400-01 (Persian) 1/1400 (Persian)
Swedish 1712-03 (Swedish) 3/1712 (Thụy Điển)
Viet. Lunar (full) 2000-01-15 (Vietnamese Lunar) Năm 2000 Tháng Giêng Ngày 15 (Vietnamese Lunar)
Viet. Lunar (year) 2000 (Vietnamese Lunar) Năm 2000 (Vietnamese Lunar)
Viet. Lunar (month) 2023-05 (Vietnamese Lunar) Năm 2023 Tháng Năm (Vietnamese Lunar)
Before (Viet. Lunar) before 1900 (Vietnamese Lunar) trước Năm 1900 (Vietnamese Lunar)
After (Viet. Lunar) after 1900 (Vietnamese Lunar) sau Năm 1900 (Vietnamese Lunar)
Can-Chi (full) 2000-01-15 (Vietnamese Lunar) Năm Canh Thìn Tháng Giêng Ngày 15 (Vietnamese Lunar)
Can-Chi (year) 2000 (Vietnamese Lunar) Năm Canh Thìn (Vietnamese Lunar)
Text only sometime in the 1800s sometime in the 1800s

dsblank and others added 8 commits June 10, 2026 20:42
Adds a new CAL_CHINESE_LUNAR (value 7) calendar type alongside the
existing Gregorian, Julian, Hebrew, French Republican, Persian, Islamic,
and Swedish calendars. Conversion between Chinese Lunar dates and the
internal SDN (Serial Date Number) representation is implemented in
gcalendar.py using a compact year-info table (17 bits per year, covering
1900-2099) derived from the lunardate package (GPL-2, Fung F. Lee,
Ricky Yeung, LI Daobing). Leap months are encoded as month + 100
(e.g. month 104 = intercalary 4th month). Month names use pinyin
romanisation (Zhengyue...Shier'yue) so they can be translated for any
locale. Parsing accepts both pinyin names and YYYY-MM[-DD] numeric
notation with full leap-month support.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When Gramps runs under a Simplified (zh_CN) or Traditional (zh_TW)
Chinese locale, Chinese Lunar calendar dates are now displayed as
native 年/月/日 strings (e.g. 1976年八月8日) instead of pinyin
romanisation. Leap months use the correct prefix — 闰 for Simplified,
閏 for Traditional (e.g. 1976年闰八月8日 for month 108).

The parsers now recognise Chinese character month names (正月, 二月 ...
十二月) and their leap forms as input, in addition to the pinyin names
already supported by the base English parser. Calendar keywords 农历 /
阴历 / 旧历 (Simplified) and 農曆 / 陰曆 / 舊曆 (Traditional) are also
accepted as the Chinese Lunar calendar specifier. Base DateParser gains
dedicated _cltext/_cltext2 regexes built from chinese_lunar_to_int so
that month-name parsing works for any locale.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces chinese_sexagenary_year(year) in gcalendar.py, which maps
any Gregorian-aligned Chinese year to its 干支 name (e.g. 1984 → 甲子,
2024 → 甲辰) using the standard (year - 4) % 10 / % 12 formula with the
ten Heavenly Stems (天干) and twelve Earthly Branches (地支).

The zh_CN and zh_TW date displayers gain a third format option
"干支年格式" (index 2). When selected, Chinese Lunar dates render as
e.g. 甲子年八月8日 or 甲辰年闰八月8日 instead of a numeric year.

Three new unit tests cover: known year values (1984 甲子, 2024 甲辰,
1900 庚子, 1949 己丑, 2025 乙巳), the 60-year cycle property, and
that all 60 stem-branch combinations appear exactly once per cycle.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In the base _display_chinese_lunar, passing a remapped date_val
(month 108 → 8) to _display_calendar caused the ISO string to show
month 08 instead of 108, breaking parser round-trips. Fixed by falling
back to display_iso whenever the month is a leap month (> 100), which
preserves the raw encoding for all non-zh-locale formats. Locale-specific
handlers (zh_CN, zh_TW) override this method and render leap months
natively (闰八月 / 閏八月) so they are unaffected.

Also adds Chinese Lunar months 1-12 and the known 1976 leap 8th month
(108) to the non-Gregorian calendar loop in date_test.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the 200-entry (1900-2099) SDN lookup table with a 9600-entry
table covering years 400-9999, generated from the tyme4py library
(MIT licence, https://github.com/6tail/tyme4py).

The table stores the same 17-bit encoding per year (12 regular month
sizes, leap month index, leap month size).  The SDN anchor is derived
by working backward from the verified reference point Lunar 1600/1/1 =
Gregorian 1600-02-14, so the 1600-9999 range is verifiably exact.
Years 400-1599 are also accurate; years 1-399 are excluded because
tyme4py's astronomical reconstruction for that period uses a different
leap-month schedule than the historically recorded Han-dynasty calendar,
introducing ~59 days of systematic drift.

The generation script scripts/gen_chinese_lunar_table.py can be run to
regenerate the table from tyme4py if the upstream library is updated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Vietnamese lunar calendar shares the same astronomical rules and
dates as the Chinese lunar calendar.  This commit adds it as a separate
calendar type (CAL_VIETNAMESE_LUNAR = 8) with Vietnamese month names
(Tháng Giêng through Tháng Chạp), leap-month prefix Nhuận, and
Can-Chi sexagenary year names (e.g. Giáp Thìn for 2024).

Changes:
- gcalendar.py: add vietnamese_lunar_sdn, vietnamese_lunar_ymd
  (wrappers over Chinese functions) and vietnamese_can_chi_year.
- date.py: add CAL_VIETNAMESE_LUNAR constant and extend CALENDARS,
  _calendar_convert, _calendar_change, calendar_names, ui_calendar_names.
- _datestrings.py: add Vietnamese month name tuple and calendar label.
- _datedisplay.py: add _display_vietnamese_lunar method.
- _dateparser.py: add _parse_vietnamese_lunar method.
- editdate.py: map CAL_VIETNAMESE_LUNAR to month name list.
- Add vietnamese_calendar_test.py with 26 tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Registers DateParserVI and DateDisplayVI for the 'vi' and 'vi_VN'
locales. Users running a Vietnamese locale can now type dates using
native Vietnamese month names (Tháng Giêng … Tháng Chạp), leap-month
prefixes (Nhuận), modifier words (trước, sau, khoảng, từ, đến), and
the calendar name "âm lịch" to select the Vietnamese Lunar calendar.

The displayer supports three formats: ISO, numeric (Năm YYYY Tháng
Giêng Ngày DD), and Can-Chi year name (Năm Giáp Thìn …). Leap months
always fall back to ISO encoding so dates round-trip without ambiguity.

A test suite covering all 12 month names, leap months, modifiers,
calendar keywords, and all three display formats is included.
Four bugs caused all Julian-calendar dates and many modifier/quality
dates to fail the datehandler round-trip test for lang='vi':

- calendar_to_int used "julius" instead of "julian", so the "(Julian)"
  calendar suffix was never recognised during parsing.
- vi.po modifier translations "sau" and "khoảng" lacked a trailing
  space, causing the modifier to be glued to the date string with no
  separator that the parser's \s+ requirement could match.
- vi.po had empty msgstr for "from " and "to ", falling back to the
  English strings which were not in modifier_to_int.
- vi.po "calculated " translation "tính toán" lacked a trailing space.
- quality_to_int used "ước tính" while the translation produces
  "phỏng chừng" for QUAL_ESTIMATED; added "phỏng chừng" as the
  primary key and kept "ước tính" as a backward-compatible alias.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Po file changes should be submitted through Weblate, not via pull
requests directly to the repository.
@dsblank dsblank force-pushed the feature/vietnamese-lunar-only branch from 382626b to 4bc7676 Compare June 12, 2026 20:12
dsblank and others added 2 commits June 12, 2026 17:40
The "calculated " quality translation in po/vi.po was missing its
trailing space, causing span dates with QUAL_CALCULATED to render
as "tính toántừ ..." instead of "tính toán từ ...", which the parser
could not round-trip.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
vi.po has missing trailing spaces for "sau"/"khoảng" and empty msgstr
for "from"/"to" (falling back to English), all of which break the
display→parse round-trip.  Override _mod_str in DateDisplayVI.__init__
with hardcoded Vietnamese strings so the displayer always produces
tokens that DateParserVI recognises, regardless of vi.po state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant