Port Formatting Japanese First Year of Era (dotnet/coreclr#19976)
authorTarek Mahmoud Sayed <tarekms@microsoft.com>
Tue, 18 Sep 2018 21:02:57 +0000 (14:02 -0700)
committerGitHub <noreply@github.com>
Tue, 18 Sep 2018 21:02:57 +0000 (14:02 -0700)
Commit migrated from https://github.com/dotnet/coreclr/commit/44fd2693a85a35d2f4e532bb5a12f145224ee5bf

src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.Defaults.cs
src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextSwitches.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs

index 5de87d5..4611019 100644 (file)
@@ -10,6 +10,8 @@ namespace System
     {
         internal static readonly string SwitchNoAsyncCurrentCulture = "Switch.System.Globalization.NoAsyncCurrentCulture";
         internal static readonly string SwitchEnforceJapaneseEraYearRanges = "Switch.System.Globalization.EnforceJapaneseEraYearRanges";
+        internal static readonly string SwitchFormatJapaneseFirstYearAsANumber = "Switch.System.Globalization.FormatJapaneseFirstYearAsANumber";
+        internal static readonly string SwitchEnforceLegacyJapaneseDateParsing = "Switch.System.Globalization.EnforceLegacyJapaneseDateParsing";
         internal static readonly string SwitchPreserveEventListnerObjectIdentity = "Switch.System.Diagnostics.EventSource.PreserveEventListnerObjectIdentity";
 
         // This is a partial method. Platforms can provide an implementation of it that will set override values
index 17c4084..57185ea 100644 (file)
@@ -29,6 +29,25 @@ namespace System
             }
         }
 
+        private static int _formatJapaneseFirstYearAsANumber;
+        public static bool FormatJapaneseFirstYearAsANumber
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get
+            {
+                return GetCachedSwitchValue(AppContextDefaultValues.SwitchFormatJapaneseFirstYearAsANumber, ref _formatJapaneseFirstYearAsANumber);
+            }
+        }
+        private static int _enforceLegacyJapaneseDateParsing;
+        public static bool EnforceLegacyJapaneseDateParsing
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get
+            {
+                return GetCachedSwitchValue(AppContextDefaultValues.SwitchEnforceLegacyJapaneseDateParsing, ref _enforceLegacyJapaneseDateParsing);
+            }
+        }
+
         private static int _preserveEventListnerObjectIdentity;
         public static bool PreserveEventListnerObjectIdentity
         {
index 5eb9f44..cb6a810 100644 (file)
@@ -2134,6 +2134,17 @@ namespace System.Globalization
         // Date separator (derived from short date format)
         internal string DateSeparator(CalendarId calendarId)
         {
+            if (calendarId == CalendarId.JAPAN && !AppContextSwitches.EnforceLegacyJapaneseDateParsing)
+            {
+                // The date separator is derived from the default short date pattern. So far this pattern is using
+                // '/' as date separator when using the Japanese calendar which make the formatting and parsing work fine.
+                // changing the default pattern is likely will happen in the near future which can easily break formatting
+                // and parsing.
+                // We are forcing here the date separator to '/' to ensure the parsing is not going to break when changing
+                // the default short date pattern. The application still can override this in the code by DateTimeFormatInfo.DateSeparartor.
+                return "/";
+            }
+
             return GetDateSeparator(ShortDates(calendarId)[0]);
         }
 
index 73fb0e9..f12a10b 100644 (file)
@@ -11,7 +11,7 @@ using System.Runtime.CompilerServices;
 
 namespace System
 {
-    /*  
+    /*
      Customized format patterns:
      P.S. Format in the table below is the internal number format used to display the pattern.
 
@@ -58,14 +58,14 @@ namespace System
         "ddd"               short weekday name (abbreviation)     Mon
         "dddd"              full weekday name                     Monday
         "dddd*"             full weekday name                     Monday
-        
+
 
         "M"     "0"         month w/o leading zero                2
         "MM"    "00"        month with leading zero               02
         "MMM"               short month name (abbreviation)       Feb
         "MMMM"              full month name                       Febuary
         "MMMM*"             full month name                       Febuary
-       
+
         "y"     "0"         two digit year (year % 100) w/o leading zero           0
         "yy"    "00"        two digit year (year % 100) with leading zero          00
         "yyy"   "D3"        year                                  2000
@@ -77,10 +77,10 @@ namespace System
         "zz"    "+00;-00"   timezone offset with leading zero     -08
         "zzz"      "+00;-00" for hour offset, "00" for minute offset  full timezone offset   -07:30
         "zzz*"  "+00;-00" for hour offset, "00" for minute offset   full timezone offset   -08:00
-        
+
         "K"    -Local       "zzz", e.g. -08:00
                -Utc         "'Z'", representing UTC
-               -Unspecified ""               
+               -Unspecified ""
                -DateTimeOffset      "zzzzz" e.g -07:30:15
 
         "g*"                the current era name                  A.D.
@@ -91,12 +91,12 @@ namespace System
         '"'                 quoted string                         "ABC" will insert ABC into the formatted string.
         "%"                 used to quote a single pattern characters      E.g.The format character "%y" is to print two digit year.
         "\"                 escaped character                     E.g. '\d' insert the character 'd' into the format string.
-        other characters    insert the character into the format string. 
+        other characters    insert the character into the format string.
 
-    Pre-defined format characters: 
+    Pre-defined format characters:
         (U) to indicate Universal time is used.
         (G) to indicate Gregorian calendar is used.
-    
+
         Format              Description                             Real format                             Example
         =========           =================================       ======================                  =======================
         "d"                 short date                              culture-specific                        10/31/1999
@@ -120,7 +120,7 @@ namespace System
 
     */
 
-    //This class contains only static members and does not require the serializable attribute.    
+    //This class contains only static members and does not require the serializable attribute.
     internal static
     class DateTimeFormat
     {
@@ -155,16 +155,16 @@ namespace System
         };
 
         ////////////////////////////////////////////////////////////////////////////
-        // 
-        // Format the positive integer value to a string and perfix with assigned 
+        //
+        // Format the positive integer value to a string and perfix with assigned
         // length of leading zero.
         //
         // Parameters:
         //  value: The value to format
-        //  len: The maximum length for leading zero.  
+        //  len: The maximum length for leading zero.
         //  If the digits of the value is greater than len, no leading zero is added.
         //
-        // Notes: 
+        // Notes:
         //  The function can format to int.MaxValue.
         //
         ////////////////////////////////////////////////////////////////////////////
@@ -252,16 +252,16 @@ namespace System
         //
         //  Action: Return the Hebrew month name for the specified DateTime.
         //  Returns: The month name string for the specified DateTime.
-        //  Arguments: 
+        //  Arguments:
         //        time   the time to format
-        //        month  The month is the value of HebrewCalendar.GetMonth(time).         
+        //        month  The month is the value of HebrewCalendar.GetMonth(time).
         //        repeat Return abbreviated month name if repeat=3, or full month name if repeat=4
         //        dtfi    The DateTimeFormatInfo which uses the Hebrew calendars as its calendar.
         //  Exceptions: None.
-        // 
+        //
 
         /* Note:
-            If DTFI is using Hebrew calendar, GetMonthName()/GetAbbreviatedMonthName() will return month names like this:            
+            If DTFI is using Hebrew calendar, GetMonthName()/GetAbbreviatedMonthName() will return month names like this:
             1   Hebrew 1st Month
             2   Hebrew 2nd Month
             ..  ...
@@ -274,7 +274,7 @@ namespace System
             12  Hebrew 11th Month
             13  Hebrew 12th Month
 
-            Therefore, if we are in a regular year, we have to increment the month name if moth is greater or eqaul to 7.            
+            Therefore, if we are in a regular year, we have to increment the month name if moth is greater or eqaul to 7.
         */
         private static string FormatHebrewMonthName(DateTime time, int month, int repeatCount, DateTimeFormatInfo dtfi)
         {
@@ -377,7 +377,7 @@ namespace System
         //
         //  Actions: Check the format to see if we should use genitive month in the formatting.
         //      Starting at the position (index) in the (format) string, look back and look ahead to
-        //      see if there is "d" or "dd".  In the case like "d MMMM" or "MMMM dd", we can use 
+        //      see if there is "d" or "dd".  In the case like "d MMMM" or "MMMM dd", we can use
         //      genitive form.  Genitive form is not used if there is more than two "d".
         //  Arguments:
         //      format      The format string to be scanned.
@@ -447,7 +447,7 @@ namespace System
         //  FormatCustomized
         //
         //  Actions: Format the DateTime instance using the specified format.
-        // 
+        //
         private static StringBuilder FormatCustomized(
             DateTime dateTime, ReadOnlySpan<char> format, DateTimeFormatInfo dtfi, TimeSpan offset, StringBuilder result)
         {
@@ -459,9 +459,10 @@ namespace System
                 resultBuilderIsPooled = true;
                 result = StringBuilderCache.Acquire();
             }
-            
+
             // This is a flag to indicate if we are format the dates using Hebrew calendar.
             bool isHebrewCalendar = (cal.ID == CalendarId.HEBREW);
+            bool isJapaneseCalendar = (cal.ID == CalendarId.JAPAN);
             // This is a flag to indicate if we are formating hour/minute/second only.
             bool bTimeOnly = true;
 
@@ -601,7 +602,7 @@ namespace System
                         bTimeOnly = false;
                         break;
                     case 'M':
-                        // 
+                        //
                         // tokenLen == 1 : Month as digits with no leading zero.
                         // tokenLen == 2 : Month as digits with leading zero for single-digit months.
                         // tokenLen == 3 : Month as a three-letter abbreviation.
@@ -653,7 +654,19 @@ namespace System
 
                         int year = cal.GetYear(dateTime);
                         tokenLen = ParseRepeatPattern(format, i, ch);
-                        if (dtfi.HasForceTwoDigitYears)
+                        if (isJapaneseCalendar &&
+                            !AppContextSwitches.FormatJapaneseFirstYearAsANumber &&
+                            year == 1 &&
+                            i + tokenLen < format.Length - 1 &&
+                            format[i + tokenLen] == '\'' &&
+                            format[i + tokenLen + 1] == DateTimeFormatInfoScanner.CJKYearSuff[0])
+                        {
+                            // We are formatting a Japanese date with year equals 1 and the year number is followed by the year sign \u5e74
+                            // In Japanese dates, the first year in the era is not formatted as a number 1 instead it is formatted as \u5143 which means
+                            // first or beginning of the era.
+                            result.Append(DateTimeFormatInfo.JapaneseEraStart[0]);
+                        }
+                        else if (dtfi.HasForceTwoDigitYears)
                         {
                             FormatDigits(result, year, tokenLen <= 2 ? tokenLen : 2);
                         }
@@ -697,7 +710,7 @@ namespace System
                         break;
                     case '%':
                         // Optional format character.
-                        // For example, format string "%d" will print day of month 
+                        // For example, format string "%d" will print day of month
                         // without leading zero.  Most of the cases, "%" can be ignored.
                         nextChar = ParseNextChar(format, i);
                         // nextChar will be -1 if we already reach the end of the format string.
@@ -726,7 +739,7 @@ namespace System
                         // Escaped character.  Can be used to insert character into the format string.
                         // For exmple, "\d" will insert the character 'd' into the string.
                         //
-                        // NOTENOTE : we can remove this format character if we enforce the enforced quote 
+                        // NOTENOTE : we can remove this format character if we enforce the enforced quote
                         // character rule.
                         // That is, we ask everyone to use single quote or double quote to insert characters,
                         // then we can remove this character.
@@ -775,7 +788,7 @@ namespace System
 
                 if (timeOnly && dateTime.Ticks < Calendar.TicksPerDay)
                 {
-                    // For time only format and a time only input, the time offset on 0001/01/01 is less 
+                    // For time only format and a time only input, the time offset on 0001/01/01 is less
                     // accurate than the system's current offset because of daylight saving time.
                     offset = TimeZoneInfo.GetLocalUtcOffset(DateTime.Now, TimeZoneInfoOptions.NoThrowOnInvalidTime);
                 }
@@ -820,8 +833,8 @@ namespace System
         private static void FormatCustomizedRoundripTimeZone(DateTime dateTime, TimeSpan offset, StringBuilder result)
         {
             // The objective of this format is to round trip the data in the type
-            // For DateTime it should round-trip the Kind value and preserve the time zone. 
-            // DateTimeOffset instance, it should do so by using the internal time zone.                        
+            // For DateTime it should round-trip the Kind value and preserve the time zone.
+            // DateTimeOffset instance, it should do so by using the internal time zone.
 
             if (offset == NullOffset)
             {
@@ -897,7 +910,7 @@ namespace System
                     realFormat = RoundtripFormat;
                     break;
                 case 'r':
-                case 'R':       // RFC 1123 Standard                    
+                case 'R':       // RFC 1123 Standard
                     realFormat = dtfi.RFC1123Pattern;
                     break;
                 case 's':       // Sortable without Time Zone Info
@@ -940,7 +953,7 @@ namespace System
                     dtfi = DateTimeFormatInfo.InvariantInfo;
                     break;
                 case 'r':
-                case 'R':       // RFC 1123 Standard                    
+                case 'R':       // RFC 1123 Standard
                     if (offset != NullOffset)
                     {
                         // Convert to UTC invariants mean this will be in range
@@ -952,7 +965,7 @@ namespace System
                     }
                     dtfi = DateTimeFormatInfo.InvariantInfo;
                     break;
-                case 's':       // Sortable without Time Zone Info                
+                case 's':       // Sortable without Time Zone Info
                     dtfi = DateTimeFormatInfo.InvariantInfo;
                     break;
                 case 'u':       // Universal time in sortable format.
@@ -1075,7 +1088,7 @@ namespace System
                     // If the time is less than 1 day, consider it as time of day.
                     // Just print out the short time format.
                     //
-                    // This is a workaround for VB, since they use ticks less then one day to be 
+                    // This is a workaround for VB, since they use ticks less then one day to be
                     // time of day.  In cultures which use calendar other than Gregorian calendar, these
                     // alternative calendar may not support ticks less than a day.
                     // For example, Japanese calendar only supports date after 1868/9/8.
index 8fc6f7b..a2665e4 100644 (file)
@@ -133,7 +133,7 @@ namespace System.Globalization
         // The string contains the default pattern.
         // When we initially construct our string[], we set the string to string[0]
 
-        // The "default" Date/time patterns 
+        // The "default" Date/time patterns
         private string longDatePattern = null;
         private string shortDatePattern = null;
         private string yearMonthPattern = null;
@@ -554,8 +554,8 @@ namespace System.Globalization
                     SR.ArgumentNull_String);
             }
 
-            // The Era Name and Abbreviated Era Name 
-            // for Taiwan Calendar on non-Taiwan SKU returns empty string (which 
+            // The Era Name and Abbreviated Era Name
+            // for Taiwan Calendar on non-Taiwan SKU returns empty string (which
             // would be matched below) but we don't want the empty string to give
             // us an Era number
             // confer 85900 DTFI.GetEra("") should fail on all cultures
@@ -583,7 +583,7 @@ namespace System.Globalization
             }
             for (int i = 0; i < AbbreviatedEraNames.Length; i++)
             {
-                // Compare the abbreviated era name in a case-insensitive way for the appropriate culture.              
+                // Compare the abbreviated era name in a case-insensitive way for the appropriate culture.
                 if (this.Culture.CompareInfo.Compare(eraName, m_abbrevEraNames[i], CompareOptions.IgnoreCase) == 0)
                 {
                     return (i + 1);
@@ -1707,7 +1707,7 @@ namespace System.Globalization
         internal const string RoundtripFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK";
         internal const string RoundtripDateTimeUnfixed = "yyyy'-'MM'-'ddTHH':'mm':'ss zzz";
 
-        // Default string isn't necessarily in our string array, so get the 
+        // Default string isn't necessarily in our string array, so get the
         // merged patterns of both
         private string[] AllYearMonthPatterns
         {
@@ -2108,7 +2108,7 @@ namespace System.Globalization
         private DateTimeFormatFlags InitializeFormatFlags()
         {
             // Build the format flags from the data in this DTFI
-            formatFlags = 
+            formatFlags =
                 (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagGenitiveMonth(
                     MonthNames, internalGetGenitiveMonthNames(false), AbbreviatedMonthNames, internalGetGenitiveMonthNames(true)) |
                 (DateTimeFormatFlags)DateTimeFormatInfoScanner.GetFormatFlagUseSpaceInMonthNames(
@@ -2232,6 +2232,8 @@ namespace System.Globalization
         internal const string CJKMinuteSuff = "\u5206";
         internal const string CJKSecondSuff = "\u79d2";
 
+        internal const string JapaneseEraStart = "\u5143";
+
         internal const string LocalTimeMark = "T";
 
         internal const string GMTName = "GMT";
@@ -2334,6 +2336,15 @@ namespace System.Globalization
                 InsertHash(temp, CJKMinuteSuff, TokenType.SEP_MinuteSuff, 0);
                 InsertHash(temp, CJKSecondSuff, TokenType.SEP_SecondSuff, 0);
 
+                if (!AppContextSwitches.EnforceLegacyJapaneseDateParsing && Calendar.ID == CalendarId.JAPAN)
+                {
+                    // We need to support parsing the dates has the start of era symbol which means it is year 1 in the era.
+                    // The start of era symbol has to be followed by the year symbol suffix, otherwise it would be invalid date.
+                    InsertHash(temp, JapaneseEraStart, TokenType.YearNumberToken, 1);
+                    InsertHash(temp, "(", TokenType.IgnorableSymbol, 0);
+                    InsertHash(temp, ")", TokenType.IgnorableSymbol, 0);
+                }
+
                 // TODO: This ignores other custom cultures that might want to do something similar
                 if (koreanLanguage)
                 {
@@ -2633,6 +2644,25 @@ namespace System.Globalization
             return (ch >= '\x0590' && ch <= '\x05ff');
         }
 
+        [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+        private bool IsAllowedJapaneseTokenFollowedByNonSpaceLetter(string tokenString, char nextCh)
+        {
+            // Allow the parser to recognize the case when having some date part followed by JapaneseEraStart "\u5143"
+            // without spaces in between. e.g. Era name followed by \u5143 in the date formats ggy.
+            // Also, allow recognizing the year suffix symbol "\u5e74" followed the JapaneseEraStart "\u5143"
+            if (!AppContextSwitches.EnforceLegacyJapaneseDateParsing && Calendar.ID == CalendarId.JAPAN &&
+                (
+                    // something like ggy, era followed by year and the year is specified using the JapaneseEraStart "\u5143"
+                    nextCh == JapaneseEraStart[0] ||
+                    // JapaneseEraStart followed by year suffix "\u5143"
+                    (tokenString == JapaneseEraStart && nextCh == CJKYearSuff[0])
+                ))
+            {
+                return true;
+            }
+            return false;
+        }
+
         internal bool Tokenize(TokenType TokenMask, out TokenType tokenType, out int tokenValue,
                                ref __DTString str)
         {
@@ -2699,12 +2729,12 @@ namespace System.Globalization
                         }
                         else if (nextCharIndex < str.Length)
                         {
-                            // Check word boundary.  The next character should NOT be a letter.
+                            // Check word boundary. The next character should NOT be a letter.
                             char nextCh = str.Value[nextCharIndex];
-                            compareStrings = !(char.IsLetter(nextCh));
+                            compareStrings = !(char.IsLetter(nextCh)) || IsAllowedJapaneseTokenFollowedByNonSpaceLetter(value.tokenString, nextCh);
                         }
                     }
-                    
+
                     if (compareStrings &&
                         ((value.tokenString.Length == 1 && str.Value[str.Index] == value.tokenString[0]) ||
                          Culture.CompareInfo.Compare(str.Value.Slice(str.Index, value.tokenString.Length), value.tokenString, CompareOptions.IgnoreCase) == 0))
index 8d703ea..b9445ad 100644 (file)
@@ -186,7 +186,7 @@ namespace System
             Debug.Assert(dtfi != null, "dtfi == null");
 
             //
-            // Do a loop through the provided formats and see if we can parse succesfully in
+            // Do a loop through the provided formats and see if we can parse successfully in
             // one of the formats.
             //
             for (int i = 0; i < formats.Length; i++)
@@ -2675,7 +2675,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 return false;
             }
 
-            // Check if the parased string only contains hour/minute/second values.
+            // Check if the parsed string only contains hour/minute/second values.
             bool bTimeOnly = (result.Year == -1 && result.Month == -1 && result.Day == -1);
 
             //
@@ -3819,6 +3819,24 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             return (DateTimeFormat.GetRealFormat(format, dtfi));
         }
 
+        [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
+        private static bool ParseJapaneseEraStart(ref __DTString str, DateTimeFormatInfo dtfi)
+        {
+            // ParseJapaneseEraStart will be called when parsing the year number. We can have dates which not listing
+            // the year as a number and listing it as JapaneseEraStart symbol (which means year 1).
+            // This will be legitimate date to recognize.
+            if (AppContextSwitches.EnforceLegacyJapaneseDateParsing || dtfi.Calendar.ID != CalendarId.JAPAN || !str.GetNext())
+                return false;
+
+            if (str.m_current != DateTimeFormatInfo.JapaneseEraStart[0])
+            {
+                str.Index--;
+                return false;
+            }
+
+            return true;
+        }
+
         private static void ConfigureFormatR(ref DateTimeFormatInfo dtfi, ref ParsingInfo parseInfo, ref DateTimeResult result)
         {
             parseInfo.calendar = GregorianCalendar.GetDefaultInstance();
@@ -3856,7 +3874,12 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 case 'y':
                     tokenLen = format.GetRepeatCount();
                     bool parseResult;
-                    if (dtfi.HasForceTwoDigitYears)
+                    if (ParseJapaneseEraStart(ref str, dtfi))
+                    {
+                        tempYear = 1;
+                        parseResult = true;
+                    }
+                    else if (dtfi.HasForceTwoDigitYears)
                     {
                         parseResult = ParseDigits(ref str, 1, 4, out tempYear);
                     }
@@ -4425,7 +4448,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         **  When the following general formats are used, the time is assumed to be in Universal time.
         **
         **Limitations:
-        **  Only GregarianCalendar is supported for now.
+        **  Only GregorianCalendar is supported for now.
         **  Only support GMT timezone.
         ==============================================================================*/
 
@@ -4592,7 +4615,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             }
 
 
-            // Check if the parased string only contains hour/minute/second values.
+            // Check if the parsed string only contains hour/minute/second values.
             bTimeOnly = (result.Year == -1 && result.Month == -1 && result.Day == -1);
             if (!CheckDefaultDateTime(ref result, ref parseInfo.calendar, styles))
             {
@@ -5218,7 +5241,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
     internal ref struct __DTString
     {
         //
-        // Value propery: stores the real string to be parsed.
+        // Value property: stores the real string to be parsed.
         //
         internal ReadOnlySpan<char> Value;
 
@@ -5230,7 +5253,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         // The length of Value string.
         internal int Length => Value.Length;
 
-        // The current chracter to be looked at.
+        // The current character to be looked at.
         internal char m_current;
 
         private CompareInfo m_info;
@@ -5564,7 +5587,7 @@ new DS[] { DS.ERROR, DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
         //      The index that contains the longest word to match
         //  Arguments:
         //      words   The string array that contains words to search.
-        //      maxMatchStrLen  [in/out] the initailized maximum length.  This parameter can be used to
+        //      maxMatchStrLen  [in/out] the initialized maximum length.  This parameter can be used to
         //          find the longest match in two string arrays.
         //
         internal int MatchLongestWords(string[] words, ref int maxMatchStrLen)