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.
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.
Related
- ROC year converter — convert any full date, with weekday and formal written form
- C# TaiwanCalendar — the .NET equivalent, as a Calendar rather than a date type
- Validating ROC date input — regex patterns and the edge cases that break naive ones
- Storing ROC dates in a database — do's and don'ts for schema design
- The Y1C problem — when ROC year 100 broke two-digit date fields in 2011
- FAQ — ROC years, lunar dates, zodiac and age
- ROC dates in Go — no stdlib support, and what to do instead