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.
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.
Related
- ROC year converter — convert any full date, with weekday and formal written form
- Java MinguoDate — the JVM equivalent, as a distinct 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