fuzzyworld

when-am-i / commit

Add Korea and North Korea calendars (lunisolar, imperial eras, Dangi, Juche)

16bbd50065f1139324156f92e31b5d73ed02c85e · Pavel Safronov · 2026-06-20 23:25:20 -0700 · parents 76ecf55eac6f

Add Korea and North Korea calendars (lunisolar, imperial eras, Dangi, Juche)

Korea covers the whole peninsula's pre-1948 history (lunisolar calendar,
then the Geonyang/Gwangmu/Yunghui regnal eras, then Gregorian) plus South
Korea's Dangi era; North Korea gets its own DPRK-only polygon for the
Dangi -> Juche -> Gregorian sequence, since Juche never applied south of
the DMZ. Adds hand-rolled Dangi/Juche converters (simple year offsets)
and a descriptive-only Korean lunisolar entry to calendars.ts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
 README.md                                          |   2 +-
 .../korea/01-korean-lunisolar-calendar.png         | Bin 0 -> 726632 bytes
 screenshots/calendars/korea/02-gregorian.png       | Bin 0 -> 657871 bytes
 screenshots/calendars/korea/03-gregorian.png       | Bin 0 -> 656367 bytes
 screenshots/calendars/korea/04-gregorian.png       | Bin 0 -> 632751 bytes
 screenshots/calendars/korea/05-gregorian.png       | Bin 0 -> 639139 bytes
 screenshots/calendars/korea/06-dangi.png           | Bin 0 -> 739425 bytes
 screenshots/calendars/north-korea/01-dangi.png     | Bin 0 -> 708754 bytes
 screenshots/calendars/north-korea/02-juche.png     | Bin 0 -> 746521 bytes
 screenshots/calendars/north-korea/03-gregorian.png | Bin 0 -> 700246 bytes
 src/data/calendars/korea.json                      | 106 +++++++++++++++++++++
 src/data/calendars/north-korea.json                |  68 +++++++++++++
 src/utils/calendars.test.ts                        |  20 ++++
 src/utils/calendars.ts                             |  43 +++++++++
 14 files changed, 238 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 68b0932..f5a0a25 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
 [![pipeline status](https://gitlab.com/PavelSafronov/when-am-i/badges/main/pipeline.svg)](https://gitlab.com/PavelSafronov/when-am-i/-/pipelines)
 [![live demo](https://img.shields.io/badge/demo-live-success)](https://pavelsafronov.gitlab.io/when-am-i)
 ![version](https://img.shields.io/badge/version-v0.1.0-blue)
-![calendar data](https://img.shields.io/badge/calendar%20data-15-orange)
+![calendar data](https://img.shields.io/badge/calendar%20data-17-orange)
 ![license](https://img.shields.io/badge/license-MIT-green)
 
 ![React](https://img.shields.io/badge/React-20232A?logo=react&logoColor=61DAFB)
diff --git a/screenshots/calendars/korea/01-korean-lunisolar-calendar.png b/screenshots/calendars/korea/01-korean-lunisolar-calendar.png
new file mode 100644
index 0000000..5ee0a13
Binary files /dev/null and b/screenshots/calendars/korea/01-korean-lunisolar-calendar.png differ
diff --git a/screenshots/calendars/korea/02-gregorian.png b/screenshots/calendars/korea/02-gregorian.png
new file mode 100644
index 0000000..f1ded9a
Binary files /dev/null and b/screenshots/calendars/korea/02-gregorian.png differ
diff --git a/screenshots/calendars/korea/03-gregorian.png b/screenshots/calendars/korea/03-gregorian.png
new file mode 100644
index 0000000..857594c
Binary files /dev/null and b/screenshots/calendars/korea/03-gregorian.png differ
diff --git a/screenshots/calendars/korea/04-gregorian.png b/screenshots/calendars/korea/04-gregorian.png
new file mode 100644
index 0000000..8121c2a
Binary files /dev/null and b/screenshots/calendars/korea/04-gregorian.png differ
diff --git a/screenshots/calendars/korea/05-gregorian.png b/screenshots/calendars/korea/05-gregorian.png
new file mode 100644
index 0000000..41b7fe4
Binary files /dev/null and b/screenshots/calendars/korea/05-gregorian.png differ
diff --git a/screenshots/calendars/korea/06-dangi.png b/screenshots/calendars/korea/06-dangi.png
new file mode 100644
index 0000000..73145c5
Binary files /dev/null and b/screenshots/calendars/korea/06-dangi.png differ
diff --git a/screenshots/calendars/north-korea/01-dangi.png b/screenshots/calendars/north-korea/01-dangi.png
new file mode 100644
index 0000000..3d525b1
Binary files /dev/null and b/screenshots/calendars/north-korea/01-dangi.png differ
diff --git a/screenshots/calendars/north-korea/02-juche.png b/screenshots/calendars/north-korea/02-juche.png
new file mode 100644
index 0000000..b5d39d7
Binary files /dev/null and b/screenshots/calendars/north-korea/02-juche.png differ
diff --git a/screenshots/calendars/north-korea/03-gregorian.png b/screenshots/calendars/north-korea/03-gregorian.png
new file mode 100644
index 0000000..c55f996
Binary files /dev/null and b/screenshots/calendars/north-korea/03-gregorian.png differ
diff --git a/src/data/calendars/korea.json b/src/data/calendars/korea.json
new file mode 100644
index 0000000..270c61e
--- /dev/null
+++ b/src/data/calendars/korea.json
@@ -0,0 +1,106 @@
+{
+    "region": {
+        "name": "Korea",
+        "geo": {
+            "type": "Polygon",
+            "coordinates": [
+                [
+                    [126.0, 34.4],
+                    [127.5, 34.3],
+                    [129.0, 35.0],
+                    [129.5, 35.6],
+                    [129.4, 37.5],
+                    [129.6, 38.6],
+                    [130.4, 39.8],
+                    [129.8, 41.0],
+                    [129.7, 41.5],
+                    [128.2, 42.0],
+                    [127.3, 41.6],
+                    [126.5, 42.0],
+                    [125.0, 40.9],
+                    [124.3, 39.9],
+                    [125.1, 38.7],
+                    [124.7, 37.7],
+                    [125.8, 37.0],
+                    [126.1, 36.0],
+                    [126.4, 35.0],
+                    [126.0, 34.4]
+                ]
+            ]
+        }
+    },
+    "eras": [
+        {
+            "name": "Korean lunisolar calendar (pre-1896)",
+            "start": null,
+            "end": "1896-01-01",
+            "calendar": "Korean lunisolar calendar",
+            "usedBy": "Koreans",
+            "description": "For centuries — through Goryeo and Joseon — Korea's civil calendar was a Chinese-derived lunisolar calendar, with months following the moon (and an intercalary month some years). Years were generally cited by Chinese imperial era names or the 60-year sexagenary cycle rather than a continuous Korean count, so there is no continuous numbered year to convert to here. King Gojong decreed the switch to the Gregorian calendar effective 1 January 1896, with the new era name Geonyang.",
+            "sources": [
+                "https://en.wikipedia.org/wiki/Korean_calendar",
+                "https://en.wikipedia.org/wiki/Korean_era_name"
+            ]
+        },
+        {
+            "name": "Geonyang era",
+            "start": "1896-01-01",
+            "end": "1897-08-12",
+            "calendar": "Gregorian",
+            "usedBy": "the Korean government under King Gojong",
+            "description": "Geonyang (\"adopting the solar calendar\") was the first Korean era name under the newly Gregorian-dated civil calendar, marking the break from the lunisolar calendar. It ran until Gojong proclaimed the Korean Empire and the Gwangmu era in October 1897 (the Gwangmu era was applied retroactively from 1897-08-13, the lunar new year that year).",
+            "sources": [
+                "https://en.wikipedia.org/wiki/Korean_era_name",
+                "https://en.wikipedia.org/wiki/Gojong_of_Korea"
+            ]
+        },
+        {
+            "name": "Gwangmu era",
+            "start": "1897-08-12",
+            "end": "1907-07-19",
+            "calendar": "Gregorian",
+            "usedBy": "the Korean Empire under Emperor Gojong",
+            "description": "Gwangmu was the era name of the Korean Empire under Emperor Gojong, declared alongside the empire's founding in October 1897 and applied from that lunar year. Dates were kept in the Gregorian calendar; the era name was the regnal label cited alongside it, similar to Japan's nengō. It ended with Gojong's forced abdication in 1907.",
+            "sources": [
+                "https://en.wikipedia.org/wiki/Korean_era_name",
+                "https://en.wikipedia.org/wiki/Korean_Empire"
+            ]
+        },
+        {
+            "name": "Yunghui era",
+            "start": "1907-07-19",
+            "end": "1910-08-29",
+            "calendar": "Gregorian",
+            "usedBy": "the Korean Empire under Emperor Sunjong",
+            "description": "Yunghui was the era name of Emperor Sunjong, the last monarch of the Korean Empire, until Japan's annexation of Korea on 29 August 1910 ended the empire's independent era-name system.",
+            "sources": [
+                "https://en.wikipedia.org/wiki/Korean_era_name",
+                "https://en.wikipedia.org/wiki/Sunjong_of_Korea"
+            ]
+        },
+        {
+            "name": "Gregorian calendar (modern)",
+            "start": "1910-08-29",
+            "end": null,
+            "calendar": "Gregorian",
+            "usedBy": "Koreans",
+            "description": "Since the end of the Korean Empire's own era names in 1910, the Gregorian calendar has remained the civil calendar across the peninsula, through Japanese colonial rule and into the modern Republic of Korea and Democratic People's Republic of Korea.",
+            "sources": [
+                "https://en.wikipedia.org/wiki/Gregorian_calendar",
+                "https://en.wikipedia.org/wiki/Korean_calendar"
+            ]
+        },
+        {
+            "name": "Dangi (Dangun era)",
+            "start": "1948-09-25",
+            "end": "1962-01-01",
+            "calendar": "Dangi",
+            "usedBy": "the Republic of Korea (South Korea)",
+            "description": "The Dangi (Dangun) era counts years from the legendary founding of Gojoseon by Dangun in 2333 BC. South Korea made it the official year count by the Reign-Title Act of 25 September 1948, used alongside the Gregorian calendar in government documents, before the Gregorian calendar became the sole official system on 1 January 1962. It remains in occasional cultural use.",
+            "sources": [
+                "https://en.wikipedia.org/wiki/Dangun_calendar",
+                "https://en.wikipedia.org/wiki/Dangun_years"
+            ]
+        }
+    ]
+}
diff --git a/src/data/calendars/north-korea.json b/src/data/calendars/north-korea.json
new file mode 100644
index 0000000..2547464
--- /dev/null
+++ b/src/data/calendars/north-korea.json
@@ -0,0 +1,68 @@
+{
+    "region": {
+        "name": "North Korea",
+        "geo": {
+            "type": "Polygon",
+            "coordinates": [
+                [
+                    [125.2, 37.7],
+                    [126.6, 37.9],
+                    [127.3, 38.3],
+                    [128.3, 38.3],
+                    [129.4, 38.6],
+                    [129.6, 39.4],
+                    [130.4, 39.8],
+                    [129.8, 41.0],
+                    [129.7, 41.5],
+                    [128.2, 42.0],
+                    [127.3, 41.6],
+                    [126.5, 42.0],
+                    [125.0, 40.9],
+                    [124.3, 39.9],
+                    [125.1, 38.7],
+                    [124.7, 37.7],
+                    [125.2, 37.7]
+                ]
+            ]
+        }
+    },
+    "eras": [
+        {
+            "name": "Dangi (Dangun era)",
+            "start": "1948-09-25",
+            "end": "1997-09-09",
+            "calendar": "Dangi",
+            "usedBy": "the Democratic People's Republic of Korea (before the Juche calendar)",
+            "description": "Like South Korea, North Korea used the Dangi (Dangun) era — counting years from the legendary 2333 BC founding of Gojoseon — alongside the Gregorian calendar, until it adopted its own Juche calendar in 1997.",
+            "sources": [
+                "https://en.wikipedia.org/wiki/Dangun_calendar",
+                "https://en.wikipedia.org/wiki/Juche_calendar"
+            ]
+        },
+        {
+            "name": "Juche calendar",
+            "start": "1997-09-09",
+            "end": "2024-10-13",
+            "calendar": "Juche",
+            "usedBy": "the Democratic People's Republic of Korea",
+            "description": "The Juche calendar counts years from 1912, the birth year of Kim Il-sung, as Juche year 1. It was decreed on 8 July 1997 (the third anniversary of Kim Il-sung's death) and put into use from 9 September 1997 (DPRK Foundation Day), used alongside the Gregorian year in state media and official documents. State media began dropping the Juche year label in October 2024, with official 2025 calendars using only the Gregorian year.",
+            "sources": [
+                "https://en.wikipedia.org/wiki/Juche_calendar",
+                "https://www.rfa.org/english/korea/2024/12/25/north-korea-juche-year-calendar/",
+                "https://www.koreatimes.co.kr/foreignaffairs/northkorea/20241017/north-korea-stops-using-juche-calendar-in-bid-to-reinforce-kims-leadership"
+            ]
+        },
+        {
+            "name": "Gregorian calendar (modern)",
+            "start": "2024-10-13",
+            "end": null,
+            "calendar": "Gregorian",
+            "usedBy": "the Democratic People's Republic of Korea",
+            "description": "From October 2024, North Korean state media and official calendars dropped the Juche year label in favor of the plain Gregorian year.",
+            "sources": [
+                "https://www.rfa.org/english/korea/2024/12/25/north-korea-juche-year-calendar/",
+                "https://www.koreatimes.co.kr/foreignaffairs/northkorea/20241017/north-korea-stops-using-juche-calendar-in-bid-to-reinforce-kims-leadership"
+            ]
+        }
+    ]
+}
diff --git a/src/utils/calendars.test.ts b/src/utils/calendars.test.ts
index b3adc1d..92a04b7 100644
--- a/src/utils/calendars.test.ts
+++ b/src/utils/calendars.test.ts
@@ -139,6 +139,26 @@ describe('convert', () => {
         expect(convert({ year: 2000, month: 1, day: 6 }, lunar).localDateLabel).toMatch(/new moon/i);
         expect(convert({ year: 2000, month: 1, day: 21 }, lunar).localDateLabel).toMatch(/full moon/i);
     });
+
+    it('converts to the Dangi (Dangun era) year (hand-rolled offset)', () => {
+        // 1948 was the year Dangi became South Korea's official year count.
+        expect(convert({ year: 1948, month: 9, day: 25 }, 'Dangi').localYearLabel).toBe('Dangi 4281');
+        expect(convert({ year: 2024, month: 1, day: 1 }, 'Dangun era').localYearLabel).toBe('Dangi 4357');
+    });
+
+    it('converts to the Juche year (hand-rolled offset)', () => {
+        // Juche year 1 = 1912 (Kim Il-sung's birth year).
+        expect(convert({ year: 1997, month: 9, day: 9 }, 'Juche').localYearLabel).toBe('Juche 86');
+        expect(convert({ year: 1912, month: 1, day: 1 }, 'Juche').localYearLabel).toBe('Juche 1');
+    });
+
+    it('Korean lunisolar calendar is descriptive-only but reports the moon', () => {
+        const c = convert({ year: 1800, month: 4, day: 1 }, 'Korean lunisolar calendar');
+        expect(c.convertible).toBe(false);
+        expect(c.system).toBe('Korean lunisolar calendar');
+        expect(c.localYearLabel).toBe('1800'); // Gregorian reference
+        expect(c.localDateLabel).toBeTruthy(); // computed moon phase
+    });
 });
 
 describe('wintersSince', () => {
diff --git a/src/utils/calendars.ts b/src/utils/calendars.ts
index 51cbe33..c4e1b92 100644
--- a/src/utils/calendars.ts
+++ b/src/utils/calendars.ts
@@ -311,6 +311,32 @@ function frenchRepublicanConverter(date: HistoricalDate): CalendarConversion {
     };
 }
 
+// ── Dangi (Dangun era) and Juche calendars (hand-rolled, arithmetic offset) ──
+// Both are simple year offsets from the Gregorian year, turning over 1 January
+// like Gregorian — not at the lunisolar new year (Seollal) — since that is how
+// they were paired with Gregorian dates in official use (Dangi 1948–1961 South
+// Korean government documents; Juche 1997–2024 North Korean state media), and
+// no source asserts a Seollal turnover for either. Dangi year 1 = 2333 BCE
+// (Dangun's legendary founding of Gojoseon); Juche year 1 = 1912 CE (Kim
+// Il-sung's birth year).
+function dangiConverter(date: HistoricalDate): CalendarConversion {
+    return {
+        system: 'Dangi (Dangun era)',
+        localYearLabel: `Dangi ${date.year + 2333}`,
+        convertible: true,
+        note: 'A solar-year offset from the Gregorian year (Dangun’s legendary founding of Gojoseon, 2333 BC); only the year is computed, not the lunisolar month and day used alongside it traditionally.',
+    };
+}
+
+function jucheConverter(date: HistoricalDate): CalendarConversion {
+    return {
+        system: 'Juche',
+        localYearLabel: `Juche ${date.year - 1911}`,
+        convertible: true,
+        note: 'A solar-year offset from the Gregorian year (Kim Il-sung’s birth year, 1912, as Juche 1).',
+    };
+}
+
 const CONVERTERS: Record<string, Converter> = {
     gregorian: (date) => ({
         system: 'Gregorian',
@@ -334,6 +360,9 @@ const CONVERTERS: Record<string, Converter> = {
     chinese: sexagenaryConverter,
     'maya long count': longCountConverter,
     'french republican calendar': frenchRepublicanConverter,
+    'dangi': dangiConverter,
+    'dangun era': dangiConverter,
+    'juche': jucheConverter,
 };
 
 for (const { names, def } of TEMPORAL_CALENDARS) {
@@ -351,6 +380,20 @@ CONVERTERS['algonquian lunar calendar'] = (date) => ({
     note: 'This calendar reckons time by moons and seasons, not a numbered year. The moon above is computed astronomically; the season-based names the Massachusett gave their moons aren’t recorded in this dataset yet.',
 });
 
+// Korea's traditional civil calendar before 1896: Chinese-derived lunisolar
+// months (same convertibility problem as Chinese sexagenary cycle, above —
+// Temporal's `chinese` calendar is broken in this polyfill, and the Korean
+// calendar can diverge from the Chinese one by up to a month), with years
+// historically named by Chinese imperial era names or the sexagenary cycle
+// rather than a continuous count of their own. Descriptive-only.
+CONVERTERS['korean lunisolar calendar'] = (date) => ({
+    system: 'Korean 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 Korean records instead cited Chinese imperial era names or the 60-year sexagenary cycle.',
+});
+
 /** Convert `date` into the named calendar's local year. */
 export function convert(date: HistoricalDate, calendarName: string): CalendarConversion {
     const converter = CONVERTERS[calendarName.trim().toLowerCase()];