diff --git a/src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs b/src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs index 10e73ee4b1d791..72db59c74402c5 100644 --- a/src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs +++ b/src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Runtime.InteropServices; internal static partial class Interop @@ -16,7 +17,7 @@ internal static extern unsafe ResultCode GetTimeZoneDisplayName( int resultLength); [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_WindowsIdToIanaId")] - internal static extern unsafe int WindowsIdToIanaId(string windowsId, [MarshalAs(UnmanagedType.LPStr)] string? region, char* ianaId, int ianaIdLength); + internal static extern unsafe int WindowsIdToIanaId(string windowsId, IntPtr region, char* ianaId, int ianaIdLength); [DllImport(Libraries.GlobalizationNative, CharSet = CharSet.Unicode, EntryPoint = "GlobalizationNative_IanaIdToWindowsId")] internal static extern unsafe int IanaIdToWindowsId(string ianaId, char* windowsId, int windowsIdLength); diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.cs index 557e333d4eb9a5..e47060d7e6aa0c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.FullGlobalizationData.cs @@ -65,8 +65,34 @@ private static unsafe bool TryConvertWindowsIdToIanaId(string windowsId, string? } } + // regionPtr will point at the region name encoded as ASCII. + IntPtr regionPtr = IntPtr.Zero; + + // Regions usually are 2 or 3 characters length. + const int MaxRegionNameLength = 11; + + // Ensure uppercasing the region as ICU require the region names be uppercased, otherwise ICU will assume default region and return unexpected result. + if (region is not null && region.Length < MaxRegionNameLength) + { + byte* regionInAscii = stackalloc byte[region.Length + 1]; + int i = 0; + for (; i < region.Length && region[i] <= '\u007F'; i++) + { + regionInAscii[i] = (uint)(region[i] - 'a') <= ('z' - 'a') ? (byte)((region[i] - 'a') + 'A') : (byte)region[i]; + } + + if (i >= region.Length) + { + regionInAscii[region.Length] = 0; + regionPtr = new IntPtr(regionInAscii); + } + + // In case getting unexpected region names, we just fallback using the default region (pasing null region name to the ICU API). + } + char* buffer = stackalloc char[100]; - int length = Interop.Globalization.WindowsIdToIanaId(windowsId, region, buffer, 100); + + int length = Interop.Globalization.WindowsIdToIanaId(windowsId, regionPtr, buffer, 100); if (length > 0) { ianaId = allocate ? new string(buffer, 0, length) : null; diff --git a/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs b/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs index 9faa4715690373..38c8bd8c3393a0 100644 --- a/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs +++ b/src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs @@ -2696,6 +2696,19 @@ public static void IdsConversionsTest(string windowsId, string ianaId) [InlineData("Central Europe Standard Time", "Europe/Tirane", "AL")] [InlineData("Central Europe Standard Time", "Europe/Podgorica", "ME")] [InlineData("Central Europe Standard Time", "Europe/Belgrade", "RS")] + // lowercased region name cases: + [InlineData("Cen. Australia Standard Time", "Australia/Adelaide", "au")] + [InlineData("AUS Central Standard Time", "Australia/Darwin", "au")] + [InlineData("E. Australia Standard Time", "Australia/Brisbane", "au")] + [InlineData("AUS Eastern Standard Time", "Australia/Sydney", "au")] + [InlineData("Tasmania Standard Time", "Australia/Hobart", "au")] + [InlineData("Romance Standard Time", "Europe/Madrid", "es")] + [InlineData("Romance Standard Time", "Europe/Madrid", "Es")] + [InlineData("Romance Standard Time", "Europe/Madrid", "eS")] + [InlineData("GMT Standard Time", "Europe/London", "gb")] + [InlineData("GMT Standard Time", "Europe/Dublin", "ie")] + [InlineData("W. Europe Standard Time", "Europe/Rome", "it")] + [InlineData("New Zealand Standard Time", "Pacific/Auckland", "nz")] [ActiveIssue("https://github.com/dotnet/runtime/issues/52072", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] public static void IdsConversionsWithRegionTest(string windowsId, string ianaId, string region) {