Skip to content

Commit 5d2e703

Browse files
authored
Improve API to help resolve canonical time zones (#178)
1 parent f34d633 commit 5d2e703

File tree

2 files changed

+96
-0
lines changed

2 files changed

+96
-0
lines changed

src/TimeZoneConverter/TZConvert.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,12 @@ static TZConvert()
4141
knownWindowsTimeZoneIds.Remove("Kamchatka Standard Time");
4242
knownWindowsTimeZoneIds.Remove("Mid-Atlantic Standard Time");
4343

44+
var knownIanaCanonicalNames = knownIanaTimeZoneNames.Except(Links.Keys).ToHashSet();
45+
4446
KnownIanaTimeZoneNames = knownIanaTimeZoneNames;
4547
KnownWindowsTimeZoneIds = knownWindowsTimeZoneIds;
4648
KnownRailsTimeZoneNames = knownRailsTimeZoneNames;
49+
KnownIanaCanonicalNames = knownIanaCanonicalNames;
4750

4851
SystemTimeZones = GetSystemTimeZones();
4952
}
@@ -63,6 +66,11 @@ static TZConvert()
6366
/// </summary>
6467
public static IReadOnlyCollection<string> KnownRailsTimeZoneNames { get; }
6568

69+
/// <summary>
70+
/// Gets a collection of all IANA canonical time zone names known to this library.
71+
/// </summary>
72+
public static IReadOnlyCollection<string> KnownIanaCanonicalNames { get; }
73+
6674
/// <summary>
6775
/// Gets a dictionary that has an sorted collection of IANA time zone names keyed by territory code.
6876
/// </summary>
@@ -410,6 +418,32 @@ public static bool TryGetTimeZoneInfo(string windowsOrIanaTimeZoneId,
410418
return false;
411419
}
412420

421+
/// <summary>
422+
/// Attempts to resolve the IANA canonical name given an IANA time zone name.
423+
/// </summary>
424+
/// <param name="ianaTimeZoneName">The IANA time zone name to resolve.</param>
425+
/// <param name="ianaCanonicalName">The resolved IANA canonical time zone name.</param>
426+
/// <returns><c>true</c> if successful, <c>false</c> otherwise.</returns>
427+
/// <remarks>
428+
/// The resolving will succeed whether the input name is an alias or a canonical name.
429+
/// </remarks>
430+
public static bool TryGetIanaCanonicalName(
431+
string ianaTimeZoneName,
432+
[MaybeNullWhen(false)] out string ianaCanonicalName)
433+
{
434+
if (ianaTimeZoneName == null || !KnownIanaTimeZoneNames.Contains(ianaTimeZoneName))
435+
{
436+
ianaCanonicalName = null;
437+
return false;
438+
}
439+
440+
ianaCanonicalName = Links.TryGetValue(ianaTimeZoneName, out ianaCanonicalName)
441+
? ianaCanonicalName
442+
: ianaTimeZoneName;
443+
444+
return true;
445+
}
446+
413447
/// <summary>
414448
/// Converts an IANA time zone name to one or more equivalent Rails time zone names.
415449
/// </summary>

test/TimeZoneConverter.Tests/TimeZoneInfoTests.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,66 @@ public void CanGetArgentinaStandardTime()
9292
var tz = TZConvert.GetTimeZoneInfo("Argentina Standard Time");
9393
Assert.NotNull(tz);
9494
}
95+
96+
[Fact]
97+
public void CanGetIanaCanonicalNameForAlias()
98+
{
99+
var result = TZConvert.TryGetIanaCanonicalName("Europe/Jersey", out var canonicalIanaName);
100+
101+
Assert.True(result, $"{nameof(TZConvert.TryGetIanaCanonicalName)} should succeed for alias.");
102+
Assert.Equal("Europe/London", canonicalIanaName);
103+
}
104+
105+
[Fact]
106+
public void CanGetIanaCanonicalNameForCanonicalName()
107+
{
108+
var result = TZConvert.TryGetIanaCanonicalName("Africa/Maputo", out var canonicalIanaName);
109+
110+
Assert.True(result, $"{nameof(TZConvert.TryGetIanaCanonicalName)} should succeed for canonical time zone name.");
111+
Assert.Equal("Africa/Maputo", canonicalIanaName);
112+
}
113+
114+
[Theory]
115+
[InlineData(null)]
116+
[InlineData("")]
117+
[InlineData(" ")]
118+
[InlineData("NOT-A-TIME-ZONE")]
119+
public void CannotGetIanaCanonicalNameForUnknownOrInvalidName(string? name)
120+
{
121+
var result = TZConvert.TryGetIanaCanonicalName(name, out _);
122+
123+
Assert.False(result, $"{nameof(TZConvert.TryGetIanaCanonicalName)} should fail for invalid or unknown time zone name.");
124+
}
125+
126+
[Fact]
127+
public void CanGetIanaCanonicalNameForAllKnownIanaTimeZones()
128+
{
129+
var names = TZConvert.KnownIanaTimeZoneNames.ToList();
130+
131+
foreach (var name in names)
132+
{
133+
var result = TZConvert.TryGetIanaCanonicalName(name, out _);
134+
135+
Assert.True(result, $"{nameof(TZConvert.TryGetIanaCanonicalName)} should succeed for \"{name}\".");
136+
}
137+
}
138+
139+
[Theory]
140+
[InlineData("Australia/Brisbane")]
141+
[InlineData("Antarctica/Troll")]
142+
[InlineData("America/Adak")]
143+
public void CanFindCanonicalTimeZoneInCanonicalNames(string name)
144+
{
145+
var result = TZConvert.KnownIanaCanonicalNames.Contains(name);
146+
147+
Assert.True(result, $"{nameof(TZConvert.KnownIanaCanonicalNames)} should contain \"{name}\".");
148+
}
149+
150+
[Fact]
151+
public void CannotFindAliasInCanonicalNames()
152+
{
153+
var result = TZConvert.KnownIanaCanonicalNames.Contains("Australia/Queensland");
154+
155+
Assert.False(result, $"{nameof(TZConvert.KnownIanaCanonicalNames)} should not contain \"Australia/Queensland\".");
156+
}
95157
}

0 commit comments

Comments
 (0)