Minguo.tw
Home / C# TaiwanCalendar

C# TaiwanCalendar: A Practical Guide

new TaiwanCalendar().GetYear(dateTime) returns the ROC year directly — 115 for 29 July 2026. .NET's System.Globalization.TaiwanCalendar wraps an ordinary DateTime and reads or writes it in ROC years, with no manual arithmetic required.

Reading an ROC year from a DateTime

TaiwanCalendar works exactly like the Gregorian calendar except that the year and era differ, according to Microsoft's own documentation, and it recognises only the current era. That means every read through it is already ROC — you never add or subtract 1911 by hand:

using System;
using System.Globalization;

var calendar = new TaiwanCalendar();
DateTime d = new DateTime(2026, 7, 29);

int rocYear = calendar.GetYear(d);   // 115
int month = calendar.GetMonth(d);    // 7
int day = calendar.GetDayOfMonth(d); // 29

Console.WriteLine($"{rocYear}/{month:00}/{day:00}"); // 115/07/29

Check a value against the ROC year converter the first time you wire this up.

Advertisement

Building a DateTime from an ROC year

The reverse direction goes through ToDateTime, which takes the ROC year, month, day, a time component and an era constant:

var calendar = new TaiwanCalendar();
DateTime d = calendar.ToDateTime(115, 7, 29, 0, 0, 0, 0, TaiwanCalendar.CurrentEra);
Console.WriteLine(d.ToString("yyyy-MM-dd")); // 2026-07-29

TaiwanCalendar.CurrentEra is inherited from the base Calendar class and is always 0; pass it rather than a literal era number, since TaiwanCalendar only recognises the current era in the first place.

Parsing an ROC date string

For a string like "115/07/29", split the parts and hand them to ToDateTime directly — there is no DateTime.ParseExact format specifier that understands ROC years on its own, so parsing still means extracting the year yourself:

string text = "115/07/29";
string[] parts = text.Split('/');
int rocYear = int.Parse(parts[0]);
int month = int.Parse(parts[1]);
int day = int.Parse(parts[2]);

var calendar = new TaiwanCalendar();
DateTime d = calendar.ToDateTime(rocYear, month, day, 0, 0, 0, 0, TaiwanCalendar.CurrentEra);

Formatting through a culture

If you want month names, separators and other locale conventions handled for you, attach a TaiwanCalendar to a CultureInfo's DateTimeFormat rather than formatting by hand. This is more setup for a single value, but pays off if you're formatting many dates consistently through the same culture object:

var culture = (CultureInfo)CultureInfo.InvariantCulture.Clone();
culture.DateTimeFormat.Calendar = new TaiwanCalendar();

DateTime d = new DateTime(2026, 7, 29);
Console.WriteLine(d.ToString("yyyy/MM/dd", culture)); // 115/07/29

Microsoft's documentation notes that if the culture is not zh-TW, era names returned by NativeCalendarName and related members come back as an empty string — a detail worth knowing if you're relying on the era text rather than just the numeric year.

The two-digit year trap

TaiwanCalendar.ToFourDigitYear(Int32) converts a two-digit year to four digits using the TwoDigitYearMax property to pick the century. This is convenient for short-form input, but it is a sliding window, not a fixed rule: if TwoDigitYearMax is left at its default and your data spans a wider range than that window covers, or if the field was truncated from three digits in the first place (a ROC 100 date stored as 00), ToFourDigitYear will resolve to the wrong century silently. Validate the source format — ideally reject two-digit ROC year fields outright — rather than relying on the default window. See the Y1C write-up for the underlying bug this guards against.

Edge cases and gotchas

TaiwanCalendar recognises only the current era. There is no BEFORE_ROC-style era to catch pre-1912 dates the way Java's MinguoEra does; a DateTime before 1912 passed through TaiwanCalendar is unsupported territory and you should check MinSupportedDateTime before trusting it.

Leap years match the Gregorian calendar exactly. Per Microsoft's documentation, a TaiwanCalendar leap year is the same Gregorian year divisible by 4, except centuries not divisible by 400 — there is no separate ROC leap-year rule to worry about.

Don't infer the calendar from a bare number. A three-digit year field could be ROC, or could be something else entirely in a system that wasn't originally Taiwan-specific. Confirm the source before wiring up TaiwanCalendar against it.

Frequently asked questions

What is TaiwanCalendar in C#?

System.Globalization.TaiwanCalendar is a built-in .NET class that reads and writes DateTime values using ROC years instead of Gregorian ones. It works exactly like the Gregorian calendar except for the year and era, and it recognises only the current era.

How do I get the ROC year from a DateTime in C#?

new TaiwanCalendar().GetYear(dateTime). For 29 July 2026 this returns 115, because .NET's TaiwanCalendar always reflects the ROC year for the underlying date, with no manual +1911 arithmetic needed.

How do I build a DateTime from an ROC year, month and day in C#?

Use ToDateTime(year, month, day, hour, minute, second, millisecond, era): new TaiwanCalendar().ToDateTime(115, 7, 29, 0, 0, 0, 0, TaiwanCalendar.CurrentEra) returns the DateTime for 29 July 2026.

How do I format a DateTime as an ROC-year string in C#?

Set a CultureInfo's DateTimeFormat.Calendar to a TaiwanCalendar instance and format through it, or compute the string manually with DateTime.Year - 1911. The culture-based approach also localises month names and separators; the manual approach is simpler when you only need the digits.

Does TaiwanCalendar handle two-digit ROC years safely?

Only within the window set by its TwoDigitYearMax property, via ToFourDigitYear(Int32). A two-digit year field that predates that window, or that was truncated from three digits at ROC 100, will resolve to the wrong century unless you validate the source data first.

Advertisement

Related