Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit e052c03

Browse files
authored
Port Formatting Japanese First Year of Era (#20729)
This is porting the changes we did in the full framework.
1 parent eb90b6c commit e052c03

File tree

6 files changed

+146
-50
lines changed

6 files changed

+146
-50
lines changed

src/mscorlib/shared/System/Globalization/CultureData.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2134,6 +2134,17 @@ internal string TimeSeparator
21342134
// Date separator (derived from short date format)
21352135
internal string DateSeparator(CalendarId calendarId)
21362136
{
2137+
if (calendarId == CalendarId.JAPAN && !AppContextSwitches.EnforceLegacyJapaneseDateParsing)
2138+
{
2139+
// The date separator is derived from the default short date pattern. So far this pattern is using
2140+
// '/' as date separator when using the Japanese calendar which make the formatting and parsing work fine.
2141+
// changing the default pattern is likely will happen in the near future which can easily break formatting
2142+
// and parsing.
2143+
// We are forcing here the date separator to '/' to ensure the parsing is not going to break when changing
2144+
// the default short date pattern. The application still can override this in the code by DateTimeFormatInfo.DateSeparartor.
2145+
return "/";
2146+
}
2147+
21372148
return GetDateSeparator(ShortDates(calendarId)[0]);
21382149
}
21392150

src/mscorlib/shared/System/Globalization/DateTimeFormat.cs

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
namespace System
1313
{
14-
/*
14+
/*
1515
Customized format patterns:
1616
P.S. Format in the table below is the internal number format used to display the pattern.
1717
@@ -58,14 +58,14 @@ Patterns Format Description Example
5858
"ddd" short weekday name (abbreviation) Mon
5959
"dddd" full weekday name Monday
6060
"dddd*" full weekday name Monday
61-
61+
6262
6363
"M" "0" month w/o leading zero 2
6464
"MM" "00" month with leading zero 02
6565
"MMM" short month name (abbreviation) Feb
6666
"MMMM" full month name Febuary
6767
"MMMM*" full month name Febuary
68-
68+
6969
"y" "0" two digit year (year % 100) w/o leading zero 0
7070
"yy" "00" two digit year (year % 100) with leading zero 00
7171
"yyy" "D3" year 2000
@@ -77,10 +77,10 @@ Patterns Format Description Example
7777
"zz" "+00;-00" timezone offset with leading zero -08
7878
"zzz" "+00;-00" for hour offset, "00" for minute offset full timezone offset -07:30
7979
"zzz*" "+00;-00" for hour offset, "00" for minute offset full timezone offset -08:00
80-
80+
8181
"K" -Local "zzz", e.g. -08:00
8282
-Utc "'Z'", representing UTC
83-
-Unspecified ""
83+
-Unspecified ""
8484
-DateTimeOffset "zzzzz" e.g -07:30:15
8585
8686
"g*" the current era name A.D.
@@ -91,12 +91,12 @@ Patterns Format Description Example
9191
'"' quoted string "ABC" will insert ABC into the formatted string.
9292
"%" used to quote a single pattern characters E.g.The format character "%y" is to print two digit year.
9393
"\" escaped character E.g. '\d' insert the character 'd' into the format string.
94-
other characters insert the character into the format string.
94+
other characters insert the character into the format string.
9595
96-
Pre-defined format characters:
96+
Pre-defined format characters:
9797
(U) to indicate Universal time is used.
9898
(G) to indicate Gregorian calendar is used.
99-
99+
100100
Format Description Real format Example
101101
========= ================================= ====================== =======================
102102
"d" short date culture-specific 10/31/1999
@@ -120,7 +120,7 @@ based on ISO 8601.
120120
121121
*/
122122

123-
//This class contains only static members and does not require the serializable attribute.
123+
//This class contains only static members and does not require the serializable attribute.
124124
internal static
125125
class DateTimeFormat
126126
{
@@ -155,16 +155,16 @@ class DateTimeFormat
155155
};
156156

157157
////////////////////////////////////////////////////////////////////////////
158-
//
159-
// Format the positive integer value to a string and perfix with assigned
158+
//
159+
// Format the positive integer value to a string and perfix with assigned
160160
// length of leading zero.
161161
//
162162
// Parameters:
163163
// value: The value to format
164-
// len: The maximum length for leading zero.
164+
// len: The maximum length for leading zero.
165165
// If the digits of the value is greater than len, no leading zero is added.
166166
//
167-
// Notes:
167+
// Notes:
168168
// The function can format to Int32.MaxValue.
169169
//
170170
////////////////////////////////////////////////////////////////////////////
@@ -252,16 +252,16 @@ private static String FormatMonth(int month, int repeatCount, DateTimeFormatInfo
252252
//
253253
// Action: Return the Hebrew month name for the specified DateTime.
254254
// Returns: The month name string for the specified DateTime.
255-
// Arguments:
255+
// Arguments:
256256
// time the time to format
257-
// month The month is the value of HebrewCalendar.GetMonth(time).
257+
// month The month is the value of HebrewCalendar.GetMonth(time).
258258
// repeat Return abbreviated month name if repeat=3, or full month name if repeat=4
259259
// dtfi The DateTimeFormatInfo which uses the Hebrew calendars as its calendar.
260260
// Exceptions: None.
261-
//
261+
//
262262

263263
/* Note:
264-
If DTFI is using Hebrew calendar, GetMonthName()/GetAbbreviatedMonthName() will return month names like this:
264+
If DTFI is using Hebrew calendar, GetMonthName()/GetAbbreviatedMonthName() will return month names like this:
265265
1 Hebrew 1st Month
266266
2 Hebrew 2nd Month
267267
.. ...
@@ -274,7 +274,7 @@ 11 Hebrew 10th Month
274274
12 Hebrew 11th Month
275275
13 Hebrew 12th Month
276276
277-
Therefore, if we are in a regular year, we have to increment the month name if moth is greater or eqaul to 7.
277+
Therefore, if we are in a regular year, we have to increment the month name if moth is greater or eqaul to 7.
278278
*/
279279
private static String FormatHebrewMonthName(DateTime time, int month, int repeatCount, DateTimeFormatInfo dtfi)
280280
{
@@ -377,7 +377,7 @@ internal static int ParseNextChar(ReadOnlySpan<char> format, int pos)
377377
//
378378
// Actions: Check the format to see if we should use genitive month in the formatting.
379379
// Starting at the position (index) in the (format) string, look back and look ahead to
380-
// see if there is "d" or "dd". In the case like "d MMMM" or "MMMM dd", we can use
380+
// see if there is "d" or "dd". In the case like "d MMMM" or "MMMM dd", we can use
381381
// genitive form. Genitive form is not used if there is more than two "d".
382382
// Arguments:
383383
// format The format string to be scanned.
@@ -447,7 +447,7 @@ private static bool IsUseGenitiveForm(ReadOnlySpan<char> format, int index, int
447447
// FormatCustomized
448448
//
449449
// Actions: Format the DateTime instance using the specified format.
450-
//
450+
//
451451
private static StringBuilder FormatCustomized(
452452
DateTime dateTime, ReadOnlySpan<char> format, DateTimeFormatInfo dtfi, TimeSpan offset, StringBuilder result)
453453
{
@@ -459,9 +459,11 @@ private static StringBuilder FormatCustomized(
459459
resultBuilderIsPooled = true;
460460
result = StringBuilderCache.Acquire();
461461
}
462-
462+
463463
// This is a flag to indicate if we are format the dates using Hebrew calendar.
464464
bool isHebrewCalendar = (cal.ID == CalendarId.HEBREW);
465+
bool isJapaneseCalendar = (cal.ID == CalendarId.JAPAN);
466+
465467
// This is a flag to indicate if we are formating hour/minute/second only.
466468
bool bTimeOnly = true;
467469

@@ -601,7 +603,7 @@ private static StringBuilder FormatCustomized(
601603
bTimeOnly = false;
602604
break;
603605
case 'M':
604-
//
606+
//
605607
// tokenLen == 1 : Month as digits with no leading zero.
606608
// tokenLen == 2 : Month as digits with leading zero for single-digit months.
607609
// tokenLen == 3 : Month as a three-letter abbreviation.
@@ -653,7 +655,19 @@ private static StringBuilder FormatCustomized(
653655

654656
int year = cal.GetYear(dateTime);
655657
tokenLen = ParseRepeatPattern(format, i, ch);
656-
if (dtfi.HasForceTwoDigitYears)
658+
if (isJapaneseCalendar &&
659+
!AppContextSwitches.FormatJapaneseFirstYearAsANumber &&
660+
year == 1 &&
661+
i + tokenLen < format.Length - 1 &&
662+
format[i + tokenLen] == '\'' &&
663+
format[i + tokenLen + 1] == DateTimeFormatInfoScanner.CJKYearSuff[0])
664+
{
665+
// We are formatting a Japanese date with year equals 1 and the year number is followed by the year sign \u5e74
666+
// In Japanese dates, the first year in the era is not formatted as a number 1 instead it is formatted as \u5143 which means
667+
// first or beginning of the era.
668+
result.Append(DateTimeFormatInfo.JapaneseEraStart[0]);
669+
}
670+
else if (dtfi.HasForceTwoDigitYears)
657671
{
658672
FormatDigits(result, year, tokenLen <= 2 ? tokenLen : 2);
659673
}
@@ -697,7 +711,7 @@ private static StringBuilder FormatCustomized(
697711
break;
698712
case '%':
699713
// Optional format character.
700-
// For example, format string "%d" will print day of month
714+
// For example, format string "%d" will print day of month
701715
// without leading zero. Most of the cases, "%" can be ignored.
702716
nextChar = ParseNextChar(format, i);
703717
// nextChar will be -1 if we already reach the end of the format string.
@@ -726,7 +740,7 @@ private static StringBuilder FormatCustomized(
726740
// Escaped character. Can be used to insert character into the format string.
727741
// For exmple, "\d" will insert the character 'd' into the string.
728742
//
729-
// NOTENOTE : we can remove this format character if we enforce the enforced quote
743+
// NOTENOTE : we can remove this format character if we enforce the enforced quote
730744
// character rule.
731745
// That is, we ask everyone to use single quote or double quote to insert characters,
732746
// then we can remove this character.
@@ -775,7 +789,7 @@ private static void FormatCustomizedTimeZone(DateTime dateTime, TimeSpan offset,
775789

776790
if (timeOnly && dateTime.Ticks < Calendar.TicksPerDay)
777791
{
778-
// For time only format and a time only input, the time offset on 0001/01/01 is less
792+
// For time only format and a time only input, the time offset on 0001/01/01 is less
779793
// accurate than the system's current offset because of daylight saving time.
780794
offset = TimeZoneInfo.GetLocalUtcOffset(DateTime.Now, TimeZoneInfoOptions.NoThrowOnInvalidTime);
781795
}
@@ -820,8 +834,8 @@ private static void FormatCustomizedTimeZone(DateTime dateTime, TimeSpan offset,
820834
private static void FormatCustomizedRoundripTimeZone(DateTime dateTime, TimeSpan offset, StringBuilder result)
821835
{
822836
// The objective of this format is to round trip the data in the type
823-
// For DateTime it should round-trip the Kind value and preserve the time zone.
824-
// DateTimeOffset instance, it should do so by using the internal time zone.
837+
// For DateTime it should round-trip the Kind value and preserve the time zone.
838+
// DateTimeOffset instance, it should do so by using the internal time zone.
825839

826840
if (offset == NullOffset)
827841
{
@@ -897,7 +911,7 @@ internal static String GetRealFormat(ReadOnlySpan<char> format, DateTimeFormatIn
897911
realFormat = RoundtripFormat;
898912
break;
899913
case 'r':
900-
case 'R': // RFC 1123 Standard
914+
case 'R': // RFC 1123 Standard
901915
realFormat = dtfi.RFC1123Pattern;
902916
break;
903917
case 's': // Sortable without Time Zone Info
@@ -940,7 +954,7 @@ private static String ExpandPredefinedFormat(ReadOnlySpan<char> format, ref Date
940954
dtfi = DateTimeFormatInfo.InvariantInfo;
941955
break;
942956
case 'r':
943-
case 'R': // RFC 1123 Standard
957+
case 'R': // RFC 1123 Standard
944958
if (offset != NullOffset)
945959
{
946960
// Convert to UTC invariants mean this will be in range
@@ -952,7 +966,7 @@ private static String ExpandPredefinedFormat(ReadOnlySpan<char> format, ref Date
952966
}
953967
dtfi = DateTimeFormatInfo.InvariantInfo;
954968
break;
955-
case 's': // Sortable without Time Zone Info
969+
case 's': // Sortable without Time Zone Info
956970
dtfi = DateTimeFormatInfo.InvariantInfo;
957971
break;
958972
case 'u': // Universal time in sortable format.
@@ -1075,7 +1089,7 @@ private static StringBuilder FormatStringBuilder(DateTime dateTime, ReadOnlySpan
10751089
// If the time is less than 1 day, consider it as time of day.
10761090
// Just print out the short time format.
10771091
//
1078-
// This is a workaround for VB, since they use ticks less then one day to be
1092+
// This is a workaround for VB, since they use ticks less then one day to be
10791093
// time of day. In cultures which use calendar other than Gregorian calendar, these
10801094
// alternative calendar may not support ticks less than a day.
10811095
// For example, Japanese calendar only supports date after 1868/9/8.

0 commit comments

Comments
 (0)