Force short date pattern to use yyyy on Linux (dotnet/coreclr#18316)
authorTarek Mahmoud Sayed <tarekms@microsoft.com>
Thu, 7 Jun 2018 18:01:26 +0000 (11:01 -0700)
committerGitHub <noreply@github.com>
Thu, 7 Jun 2018 18:01:26 +0000 (11:01 -0700)
* Force short date pattern to use yyyy on Linux

The default pattern we get is using yy which causes the years  to be displayed  as 2 digits. This is not acceptable for many users. The fix here is to force 4-digit year as a default and still keep the original pattern which has 2-digits year in the optional list

* Address the review feedback

Commit migrated from https://github.com/dotnet/coreclr/commit/a51328304a95a1a5e0eb037883176dff248dc687

src/libraries/System.Private.CoreLib/src/System/Globalization/CalendarData.Unix.cs

index 17d6ed7..1d37926 100644 (file)
@@ -134,16 +134,107 @@ namespace System.Globalization
             {
                 List<string> datePatternsList = callbackContext.Results;
 
-                datePatterns = new string[datePatternsList.Count];
                 for (int i = 0; i < datePatternsList.Count; i++)
                 {
-                    datePatterns[i] = NormalizeDatePattern(datePatternsList[i]);
+                    datePatternsList[i] = NormalizeDatePattern(datePatternsList[i]);
                 }
+
+                if (dataType == CalendarDataType.ShortDates)
+                    FixDefaultShortDatePattern(datePatternsList);
+
+                datePatterns = datePatternsList.ToArray();
             }
 
             return result;
         }
 
+        // FixDefaultShortDatePattern will convert the default short date pattern from using 'yy' to using 'yyyy'
+        // And will ensure the original pattern still exist in the list.
+        // doing that will have the short date pattern format the year as 4-digit number and not just 2-digit number.
+        // Example: June 5, 2018 will be formatted to something like 6/5/2018 instead of 6/5/18 fro en-US culture.
+        private static void FixDefaultShortDatePattern(List<string> shortDatePatterns)
+        {
+            if (shortDatePatterns.Count == 0)
+                return;
+
+            string s = shortDatePatterns[0];
+
+            // We are not expecting any pattern have length more than 100.
+            // We have to do this check to prevent stack overflow as we allocate the buffer on the stack.
+            if (s.Length > 100)
+                return;
+
+            Span<char> modifiedPattern = stackalloc char[s.Length + 2];
+            int index = 0;
+
+            while (index < s.Length)
+            {
+                if (s[index] == '\'')
+                {
+                    do 
+                    {
+                        modifiedPattern[index] = s[index];
+                        index++;
+                    } while (index < s.Length && s[index] != '\'');
+
+                    if (index >= s.Length)
+                        return;                 
+                }
+                else if (s[index] == 'y')
+                {
+                    modifiedPattern[index] = 'y';
+                    break;
+                }
+
+                modifiedPattern[index] = s[index];
+                index++;
+            }
+
+            if (index >= s.Length - 1 || s[index + 1] != 'y')
+            {
+                // not a 'yy' pattern
+                return;
+            }
+
+            if (index + 2 < s.Length && s[index + 2] == 'y')
+            {
+                // we have 'yyy' then nothing to do
+                return;
+            }
+
+            // we are sure now we have 'yy' pattern
+
+            Debug.Assert(index + 3 < modifiedPattern.Length);
+
+            modifiedPattern[index + 1] = 'y'; // second y
+            modifiedPattern[index + 2] = 'y'; // third y
+            modifiedPattern[index + 3] = 'y'; // fourth y
+
+            index += 2;
+
+            // Now, copy the rest of the pattern to the destination buffer
+            while (index < s.Length)
+            {
+                modifiedPattern[index + 2] = s[index];
+                index++;
+            }
+
+            shortDatePatterns[0] = modifiedPattern.ToString();
+
+            for (int i = 1; i < shortDatePatterns.Count; i++)
+            {
+                if (shortDatePatterns[i] == shortDatePatterns[0])
+                {
+                    // Found match in the list to the new constructed pattern, then replace it with the original modified pattern
+                    shortDatePatterns[i] = s;
+                    return;
+                }
+            }
+
+            // if we come here means the newly constructed pattern not found on the list, then add the original pattern
+            shortDatePatterns.Add(s);
+        }
+
         /// <summary>
         /// The ICU date format characters are not exactly the same as the .NET date format characters.
         /// NormalizeDatePattern will take in an ICU date pattern and return the equivalent .NET date pattern.