Update RegionInfo data and Fix RegionInfo.CurrentRegion on Windows (#33834)
authorTarek Mahmoud Sayed <tarekms@microsoft.com>
Sat, 21 Mar 2020 00:49:24 +0000 (17:49 -0700)
committerGitHub <noreply@github.com>
Sat, 21 Mar 2020 00:49:24 +0000 (17:49 -0700)
* Update RegionInfo data and Fix RegionInfo.CurrentRegion on Windows

* Address the feedback

* feedback

* More Feedback addressing

src/coreclr/src/System.Private.CoreLib/PinvokeAnalyzerExceptionList.analyzerdata
src/libraries/Common/src/Interop/Windows/Kernel32/Interop.Globalization.cs
src/libraries/Common/tests/CoreFx.Private.TestUtilities/System/PlatformDetection.cs
src/libraries/System.Globalization/tests/CultureInfo/CultureInfoAll.cs
src/libraries/System.Globalization/tests/System/Globalization/RegionInfoTests.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Unix.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Windows.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/RegionInfo.cs

index 5d45345..141a5c2 100644 (file)
@@ -5,3 +5,6 @@ normaliz.dll!NormalizeString
 <!-- On Nano these are stubs that return failure -->
 user32.dll!GetProcessWindowStation
 user32.dll!GetUserObjectInformationW
+
+<!-- GetGeoInfo is supported by the analyzer complain for nit using GetGeoInfoW instead and we need to keep the style of not using 'W' in the names -->
+kernel32.dll!GetGeoInfo
\ No newline at end of file
index 13c56f1..608073d 100644 (file)
@@ -40,6 +40,10 @@ internal static partial class Interop
 
         internal const uint TIME_NOSECONDS = 0x00000002;
 
+        internal const int GEOCLASS_NATION       = 16;
+        internal const int GEO_ISO2              =  4;
+        internal const int GEOID_NOT_AVAILABLE   = -1;
+
         internal const string LOCALE_NAME_USER_DEFAULT = null;
         internal const string LOCALE_NAME_SYSTEM_DEFAULT = "!x-sys-default-locale";
 
@@ -133,6 +137,12 @@ internal static partial class Interop
         [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
         internal static extern int GetCalendarInfoEx(string? lpLocaleName, uint Calendar, IntPtr lpReserved, uint CalType, IntPtr lpCalData, int cchData, IntPtr lpValue);
 
+        [DllImport("kernel32.dll")]
+        internal static extern int GetUserGeoID(int geoClass);
+
+        [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
+        internal static extern int GetGeoInfo(int location, int geoType, char* lpGeoData, int cchData, int LangId);
+
         [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
         internal static extern bool EnumCalendarInfoExEx(EnumCalendarInfoProcExEx pCalInfoEnumProcExEx, string lpLocaleName, uint Calendar, string? lpReserved, uint CalType, void* lParam);
 
index 088f7ee..3f021d3 100644 (file)
@@ -33,6 +33,7 @@ namespace System
         public static bool IsArgIteratorSupported => IsMonoRuntime || (IsWindows && IsNotArmProcess);
         public static bool IsArgIteratorNotSupported => !IsArgIteratorSupported;
         public static bool Is32BitProcess => IntPtr.Size == 4;
+        public static bool IsNotWindows => !IsWindows;
 
         // Please make sure that you have the libgdiplus dependency installed.
         // For details, see https://docs.microsoft.com/dotnet/core/install/dependencies?pivots=os-macos&tabs=netcore31#libgdiplus
@@ -206,7 +207,7 @@ namespace System
         private static bool GetIsRunningOnMonoInterpreter()
         {
             // This is a temporary solution because mono does not support interpreter detection
-            // within the runtime.  
+            // within the runtime.
             var val = Environment.GetEnvironmentVariable("MONO_ENV_OPTIONS");
             return (val != null && val.Contains("--interpreter"));
         }
index f20cecc..028cf71 100644 (file)
@@ -6,6 +6,7 @@ using System.ComponentModel;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.InteropServices;
+using Microsoft.DotNet.RemoteExecutor;
 using System.Text;
 using Xunit;
 
@@ -631,10 +632,13 @@ namespace System.Globalization.Tests
         [Fact]
         public void ClearCachedDataTest()
         {
-            CultureInfo ci = CultureInfo.GetCultureInfo("ja-JP");
-            Assert.True((object) ci == (object) CultureInfo.GetCultureInfo("ja-JP"), "Expected getting same object reference");
-            ci.ClearCachedData();
-            Assert.False((object) ci == (object) CultureInfo.GetCultureInfo("ja-JP"), "expected to get a new object reference");
+            RemoteExecutor.Invoke(() =>
+            {
+                CultureInfo ci = CultureInfo.GetCultureInfo("ja-JP");
+                Assert.True((object) ci == (object) CultureInfo.GetCultureInfo("ja-JP"), "Expected getting same object reference");
+                ci.ClearCachedData();
+                Assert.False((object) ci == (object) CultureInfo.GetCultureInfo("ja-JP"), "expected to get a new object reference");
+            }).Dispose();
         }
 
         [Fact]
index 204365e..f729526 100644 (file)
@@ -52,7 +52,7 @@ namespace System.Globalization.Tests
             AssertExtensions.Throws<ArgumentException>("name", () => new RegionInfo(name));
         }
 
-        [Fact]
+        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows))]
         public void CurrentRegion()
         {
             using (new ThreadCultureChange("en-US"))
@@ -63,6 +63,21 @@ namespace System.Globalization.Tests
             }
         }
 
+        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows))]
+        public void TestCurrentRegion()
+        {
+            RemoteExecutor.Invoke(() =>
+            {
+                RegionInfo ri = RegionInfo.CurrentRegion;
+                CultureInfo.CurrentCulture.ClearCachedData(); // clear the current region cached data
+
+                CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("ja-JP");
+
+                // Changing the current culture shouldn't affect the default current region as we get it from Windows settings.
+                Assert.Equal(ri.TwoLetterISORegionName, RegionInfo.CurrentRegion.TwoLetterISORegionName);
+            }).Dispose();
+        }
+
         [Theory]
         [InlineData("en-US", "United States")]
         [OuterLoop("May fail on machines with multiple language packs installed")] // see https://github.com/dotnet/runtime/issues/30132
index e429353..f3c3e5d 100644 (file)
@@ -418,5 +418,7 @@ namespace System.Globalization
         internal bool IsWin32Installed => false;
 
         internal bool IsReplacementCulture => false;
+
+        internal static CultureData GetCurrentRegionData() => CultureInfo.CurrentCulture._cultureData;
     }
 }
index b8ae408..48e4552 100644 (file)
@@ -7,6 +7,7 @@ using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Text;
 using Internal.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 
 namespace System.Globalization
 {
@@ -714,5 +715,33 @@ namespace System.Globalization
                 return false;
             }
         }
+
+        internal static unsafe CultureData GetCurrentRegionData()
+        {
+            Span<char> geoIso2Letters = stackalloc char[10];
+
+            int geoId = Interop.Kernel32.GetUserGeoID(Interop.Kernel32.GEOCLASS_NATION);
+            if (geoId != Interop.Kernel32.GEOID_NOT_AVAILABLE)
+            {
+                int geoIsoIdLength;
+                fixed (char* pGeoIsoId = geoIso2Letters)
+                {
+                    geoIsoIdLength = Interop.Kernel32.GetGeoInfo(geoId, Interop.Kernel32.GEO_ISO2, pGeoIsoId, geoIso2Letters.Length, 0);
+                }
+
+                if (geoIsoIdLength != 0)
+                {
+                    geoIsoIdLength -= geoIso2Letters[geoIsoIdLength - 1] == 0 ? 1 : 0; // handle null termination and exclude it.
+                    CultureData? cd = GetCultureDataForRegion(geoIso2Letters.Slice(0, geoIsoIdLength).ToString(), true);
+                    if (cd != null)
+                    {
+                        return cd;
+                    }
+                }
+            }
+
+            // Fallback to current locale data.
+            return CultureInfo.CurrentCulture._cultureData;
+        }
     }
 }
index 90a502b..70fc788 100644 (file)
@@ -152,135 +152,262 @@ namespace System.Globalization
         /// </remarks>
         private static Dictionary<string, string> RegionNames =>
             s_regionNames ??=
-            new Dictionary<string, string>(211 /* prime */)
+            new Dictionary<string, string>(257 /* prime */)
             {
+                { "001", "en-001" },
                 { "029", "en-029" },
+                { "150", "en-150" },
+                { "419", "es-419" },
+                { "AD", "ca-AD" },
                 { "AE", "ar-AE" },
                 { "AF", "prs-AF" },
+                { "AG", "en-AG" },
+                { "AI", "en-AI" },
                 { "AL", "sq-AL" },
                 { "AM", "hy-AM" },
+                { "AO", "pt-AO" },
+                { "AQ", "en-A" },
                 { "AR", "es-AR" },
+                { "AS", "en-AS" },
                 { "AT", "de-AT" },
                 { "AU", "en-AU" },
+                { "AW", "nl-AW" },
+                { "AX", "sv-AX" },
                 { "AZ", "az-Cyrl-AZ" },
                 { "BA", "bs-Latn-BA" },
+                { "BB", "en-BB" },
                 { "BD", "bn-BD" },
                 { "BE", "nl-BE" },
+                { "BF", "fr-BF" },
                 { "BG", "bg-BG" },
                 { "BH", "ar-BH" },
+                { "BI", "rn-BI" },
+                { "BJ", "fr-BJ" },
+                { "BL", "fr-BL" },
+                { "BM", "en-BM" },
                 { "BN", "ms-BN" },
                 { "BO", "es-BO" },
+                { "BQ", "nl-BQ" },
                 { "BR", "pt-BR" },
+                { "BS", "en-BS" },
+                { "BT", "dz-BT" },
+                { "BV", "nb-B" },
+                { "BW", "en-BW" },
                 { "BY", "be-BY" },
                 { "BZ", "en-BZ" },
                 { "CA", "en-CA" },
+                { "CC", "en-CC" },
+                { "CD", "fr-CD" },
+                { "CF", "sg-CF" },
+                { "CG", "fr-CG" },
                 { "CH", "it-CH" },
+                { "CI", "fr-CI" },
+                { "CK", "en-CK" },
                 { "CL", "es-CL" },
+                { "CM", "fr-C" },
                 { "CN", "zh-CN" },
                 { "CO", "es-CO" },
                 { "CR", "es-CR" },
                 { "CS", "sr-Cyrl-CS" },
+                { "CU", "es-CU" },
+                { "CV", "pt-CV" },
+                { "CW", "nl-CW" },
+                { "CX", "en-CX" },
+                { "CY", "el-CY" },
                 { "CZ", "cs-CZ" },
                 { "DE", "de-DE" },
+                { "DJ", "fr-DJ" },
                 { "DK", "da-DK" },
+                { "DM", "en-DM" },
                 { "DO", "es-DO" },
                 { "DZ", "ar-DZ" },
                 { "EC", "es-EC" },
                 { "EE", "et-EE" },
                 { "EG", "ar-EG" },
+                { "ER", "tig-ER" },
                 { "ES", "es-ES" },
                 { "ET", "am-ET" },
                 { "FI", "fi-FI" },
+                { "FJ", "en-FJ" },
+                { "FK", "en-FK" },
+                { "FM", "en-FM" },
                 { "FO", "fo-FO" },
                 { "FR", "fr-FR" },
+                { "GA", "fr-GA" },
                 { "GB", "en-GB" },
+                { "GD", "en-GD" },
                 { "GE", "ka-GE" },
+                { "GF", "fr-GF" },
+                { "GG", "en-GG" },
+                { "GH", "en-GH" },
+                { "GI", "en-GI" },
                 { "GL", "kl-GL" },
+                { "GM", "en-GM" },
+                { "GN", "fr-GN" },
+                { "GP", "fr-GP" },
+                { "GQ", "es-GQ" },
                 { "GR", "el-GR" },
+                { "GS", "en-G" },
                 { "GT", "es-GT" },
+                { "GU", "en-GU" },
+                { "GW", "pt-GW" },
+                { "GY", "en-GY" },
                 { "HK", "zh-HK" },
+                { "HM", "en-H" },
                 { "HN", "es-HN" },
                 { "HR", "hr-HR" },
+                { "HT", "fr-HT" },
                 { "HU", "hu-HU" },
                 { "ID", "id-ID" },
                 { "IE", "en-IE" },
                 { "IL", "he-IL" },
+                { "IM", "gv-IM" },
                 { "IN", "hi-IN" },
+                { "IO", "en-IO" },
                 { "IQ", "ar-IQ" },
                 { "IR", "fa-IR" },
                 { "IS", "is-IS" },
                 { "IT", "it-IT" },
                 { "IV", "" },
+                { "JE", "en-JE" },
                 { "JM", "en-JM" },
                 { "JO", "ar-JO" },
                 { "JP", "ja-JP" },
                 { "KE", "sw-KE" },
                 { "KG", "ky-KG" },
                 { "KH", "km-KH" },
+                { "KI", "en-KI" },
+                { "KM", "ar-KM" },
+                { "KN", "en-KN" },
+                { "KP", "ko-KP" },
                 { "KR", "ko-KR" },
                 { "KW", "ar-KW" },
+                { "KY", "en-KY" },
                 { "KZ", "kk-KZ" },
                 { "LA", "lo-LA" },
                 { "LB", "ar-LB" },
+                { "LC", "en-LC" },
                 { "LI", "de-LI" },
                 { "LK", "si-LK" },
+                { "LR", "en-LR" },
+                { "LS", "st-LS" },
                 { "LT", "lt-LT" },
                 { "LU", "lb-LU" },
                 { "LV", "lv-LV" },
                 { "LY", "ar-LY" },
                 { "MA", "ar-MA" },
                 { "MC", "fr-MC" },
+                { "MD", "ro-MD" },
                 { "ME", "sr-Latn-ME" },
+                { "MF", "fr-MF" },
+                { "MG", "mg-MG" },
+                { "MH", "en-MH" },
                 { "MK", "mk-MK" },
+                { "ML", "fr-ML" },
+                { "MM", "my-MM" },
                 { "MN", "mn-MN" },
                 { "MO", "zh-MO" },
+                { "MP", "en-MP" },
+                { "MQ", "fr-MQ" },
+                { "MR", "ar-MR" },
+                { "MS", "en-MS" },
                 { "MT", "mt-MT" },
+                { "MU", "en-MU" },
                 { "MV", "dv-MV" },
+                { "MW", "en-MW" },
                 { "MX", "es-MX" },
                 { "MY", "ms-MY" },
+                { "MZ", "pt-MZ" },
+                { "NA", "en-NA" },
+                { "NC", "fr-NC" },
+                { "NE", "fr-NE" },
+                { "NF", "en-NF" },
                 { "NG", "ig-NG" },
                 { "NI", "es-NI" },
                 { "NL", "nl-NL" },
                 { "NO", "nn-NO" },
                 { "NP", "ne-NP" },
+                { "NR", "en-NR" },
+                { "NU", "en-NU" },
                 { "NZ", "en-NZ" },
                 { "OM", "ar-OM" },
                 { "PA", "es-PA" },
                 { "PE", "es-PE" },
+                { "PF", "fr-PF" },
+                { "PG", "en-PG" },
                 { "PH", "en-PH" },
                 { "PK", "ur-PK" },
                 { "PL", "pl-PL" },
+                { "PM", "fr-PM" },
+                { "PN", "en-PN" },
                 { "PR", "es-PR" },
+                { "PS", "ar-PS" },
                 { "PT", "pt-PT" },
+                { "PW", "en-PW" },
                 { "PY", "es-PY" },
                 { "QA", "ar-QA" },
+                { "RE", "fr-RE" },
                 { "RO", "ro-RO" },
                 { "RS", "sr-Latn-RS" },
                 { "RU", "ru-RU" },
                 { "RW", "rw-RW" },
                 { "SA", "ar-SA" },
+                { "SB", "en-SB" },
+                { "SC", "fr-SC" },
+                { "SD", "ar-SD" },
                 { "SE", "sv-SE" },
                 { "SG", "zh-SG" },
+                { "SH", "en-SH" },
                 { "SI", "sl-SI" },
+                { "SJ", "nb-SJ" },
                 { "SK", "sk-SK" },
+                { "SL", "en-SL" },
+                { "SM", "it-SM" },
                 { "SN", "wo-SN" },
+                { "SO", "so-SO" },
+                { "SR", "nl-SR" },
+                { "SS", "en-SS" },
+                { "ST", "pt-ST" },
                 { "SV", "es-SV" },
+                { "SX", "nl-SX" },
                 { "SY", "ar-SY" },
+                { "SZ", "ss-SZ" },
+                { "TC", "en-TC" },
+                { "TD", "fr-TD" },
+                { "TF", "fr-T" },
+                { "TG", "fr-TG" },
                 { "TH", "th-TH" },
                 { "TJ", "tg-Cyrl-TJ" },
+                { "TK", "en-TK" },
+                { "TL", "pt-TL" },
                 { "TM", "tk-TM" },
                 { "TN", "ar-TN" },
+                { "TO", "to-TO" },
                 { "TR", "tr-TR" },
                 { "TT", "en-TT" },
+                { "TV", "en-TV" },
                 { "TW", "zh-TW" },
+                { "TZ", "sw-TZ" },
                 { "UA", "uk-UA" },
+                { "UG", "sw-UG" },
+                { "UM", "en-UM" },
                 { "US", "en-US" },
                 { "UY", "es-UY" },
                 { "UZ", "uz-Cyrl-UZ" },
+                { "VA", "it-VA" },
+                { "VC", "en-VC" },
                 { "VE", "es-VE" },
+                { "VG", "en-VG" },
+                { "VI", "en-VI" },
                 { "VN", "vi-VN" },
+                { "VU", "fr-VU" },
+                { "WF", "fr-WF" },
+                { "WS", "en-WS" },
+                { "XK", "sq-XK" },
                 { "YE", "ar-YE" },
+                { "YT", "fr-YT" },
                 { "ZA", "af-ZA" },
+                { "ZM", "en-ZM" },
                 { "ZW", "en-ZW" }
             };
 
index ccaa806..47894cc 100644 (file)
@@ -96,7 +96,7 @@ namespace System.Globalization
                 RegionInfo? temp = s_currentRegionInfo;
                 if (temp == null)
                 {
-                    temp = new RegionInfo(CultureInfo.CurrentCulture._cultureData);
+                    temp = new RegionInfo(CultureData.GetCurrentRegionData());
 
                     // Need full name for custom cultures
                     temp._name = temp._cultureData.RegionName;