Skip to content

Commit

Permalink
Fix Time Zone Id Conversion with Lowercased Regions (#53315)
Browse files Browse the repository at this point in the history
Co-authored-by: Martin Costello <martin@martincostello.com>
  • Loading branch information
tarekgh and martincostello authored May 28, 2021
1 parent 33b0f24 commit e3f0144
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 2 deletions.
3 changes: 2 additions & 1 deletion src/libraries/Common/src/Interop/Interop.TimeZoneInfo.cs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
13 changes: 13 additions & 0 deletions src/libraries/System.Runtime/tests/System/TimeZoneInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down

0 comments on commit e3f0144

Please sign in to comment.