Minguo.tw
Home / Java MinguoDate

Java MinguoDate: A Practical Guide

MinguoDate.of(115, 7, 29) represents 29 July 2026. Java has a proper built-in ROC calendar type — java.time.chrono.MinguoDate, since Java 8 — unlike most languages, but it is a separate type from LocalDate and needs converting at the boundary.

Creating and reading a MinguoDate

MinguoDate lives in java.time.chrono and implements ChronoLocalDate, so it shares most of the API shape of LocalDate but counts years from the founding of the Republic of China: Minguo year 1 is ISO year 1912.

import java.time.chrono.MinguoDate;
import java.time.chrono.MinguoEra;

MinguoDate d = MinguoDate.of(115, 7, 29);
System.out.println(d);              // Minguo ROC 115-07-29
System.out.println(d.getEra());     // ROC
System.out.println(d.get(java.time.temporal.ChronoField.YEAR)); // 115

MinguoDate today = MinguoDate.now(); // current date in the Minguo calendar

Cross-check a converted value against the ROC year converter while you're testing.

Advertisement

Converting between MinguoDate and LocalDate

Most of a typical codebase works in LocalDate, so MinguoDate usually only appears at the edges — parsing a form field, formatting a report. Conversion in both directions is exact, because both types are backed by the same epoch-day representation:

import java.time.LocalDate;
import java.time.chrono.MinguoDate;

MinguoDate roc = MinguoDate.of(115, 7, 29);
LocalDate iso = LocalDate.from(roc);
System.out.println(iso); // 2026-07-29

LocalDate someDate = LocalDate.of(2026, 7, 29);
MinguoDate backToRoc = MinguoDate.from(someDate);
System.out.println(backToRoc); // Minguo ROC 115-07-29

Parsing an ROC date string

DateTimeFormatter can parse directly into a MinguoDate if you tell it which chronology to use, which avoids writing your own year-offset arithmetic for well-formed input:

import java.time.chrono.MinguoChronology;
import java.time.chrono.MinguoDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;

DateTimeFormatter rocFormatter = new DateTimeFormatterBuilder()
    .appendPattern("uuu/MM/dd")
    .toFormatter()
    .withChronology(MinguoChronology.INSTANCE);

MinguoDate parsed = MinguoDate.from(rocFormatter.parse("115/07/29"));
System.out.println(parsed); // Minguo ROC 115-07-29

The pattern letter u (not y) is deliberate: with a non-ISO chronology, y refers to "year of era" while u is the proleptic year, and mixing them up produces confusing results for pre-ROC dates. For a fixed-width 115/07/29 string this distinction rarely bites, but it matters the moment before-ROC dates enter the picture.

Formatting a MinguoDate for display

DateTimeFormatter display = DateTimeFormatter.ofPattern("Gy/MM/dd")
    .withChronology(MinguoChronology.INSTANCE);

System.out.println(display.format(MinguoDate.of(115, 7, 29)));
// 民國115/07/29  (era symbol depends on locale)

The G pattern letter prints the era name, which for the Minguo chronology is locale-dependent — test the exact output string your application needs rather than assuming a fixed literal.

Before-ROC dates and the two eras

The Minguo chronology defines exactly two eras: MinguoEra.ROC for year 1 (1912) onward, and MinguoEra.BEFORE_ROC for anything earlier. There is no year zero in either era, matching how the calendar is actually used on Taiwanese documents:

MinguoDate beforeRoc = MinguoDate.of(-1, 1, 1); // 1 Jan, 2 years before ROC 1 => ISO 1910
System.out.println(beforeRoc.getEra()); // BEFORE_ROC

Most application code never constructs a BEFORE_ROC date deliberately, but a parser fed unexpected input can produce one silently. Check getEra() if a downstream calculation on a user-supplied date looks wrong.

Edge cases and gotchas

Don't confuse MinguoDate with a formatted string. MinguoDate is a real chronology-aware date type, not a display helper — if you only need to show a LocalDate as an ROC year without ever manipulating it as Minguo, a formatter with .withChronology(MinguoChronology.INSTANCE) on the existing LocalDate is simpler than converting the object itself.

Two-digit year fields truncate at ROC 100 regardless of which Java type reads them. If you're parsing a legacy fixed-width import, see the Y1C write-up for how to detect it before it reaches MinguoDate.of().

Comparisons across chronologies need care. MinguoDate.compareTo() only compares with other MinguoDate instances of the same chronology; comparing dates from different chronologies should go through ChronoField.EPOCH_DAY rather than the natural ordering, per the JDK documentation.

Frequently asked questions

What is MinguoDate in Java?

java.time.chrono.MinguoDate is a built-in JDK class (since Java 8) representing a date in Taiwan's ROC calendar. It implements ChronoLocalDate, so it interoperates with the rest of java.time, but it is a distinct type from LocalDate.

How do I create a MinguoDate for ROC year 115, 7 July 29?

MinguoDate.of(115, 7, 29). The first argument is the ROC proleptic year, not a Gregorian year — MinguoDate.of(115, 7, 29) represents 29 July 2026, because Minguo year 1 equals ISO year 1912.

How do I convert a MinguoDate to a LocalDate?

Use LocalDate.from(minguoDate). Both implement the same ChronoLocalDate interface backed by the same epoch day, so the conversion is exact and loses no information.

How do I convert a LocalDate to a MinguoDate?

Use MinguoDate.from(localDate), or MinguoChronology.INSTANCE.date(localDate). Both produce the equivalent ROC-calendar date from an existing ISO LocalDate.

What does MinguoDate.getEra() return for a date before 1912?

MinguoEra.BEFORE_ROC. The Minguo chronology has exactly two eras, MinguoEra.ROC for year 1 (1912) onward and MinguoEra.BEFORE_ROC for anything earlier; there is no year zero in either era.

Advertisement

Related