Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/TimeZoneConverter/TZConvert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ static TZConvert()
knownWindowsTimeZoneIds.Remove("Kamchatka Standard Time");
knownWindowsTimeZoneIds.Remove("Mid-Atlantic Standard Time");

var knownIanaCanonicalNames = knownIanaTimeZoneNames.Except(Links.Keys).ToHashSet();

KnownIanaTimeZoneNames = knownIanaTimeZoneNames;
KnownWindowsTimeZoneIds = knownWindowsTimeZoneIds;
KnownRailsTimeZoneNames = knownRailsTimeZoneNames;
KnownIanaCanonicalNames = knownIanaCanonicalNames;

SystemTimeZones = GetSystemTimeZones();
}
Expand All @@ -63,6 +66,11 @@ static TZConvert()
/// </summary>
public static IReadOnlyCollection<string> KnownRailsTimeZoneNames { get; }

/// <summary>
/// Gets a collection of all IANA canonical time zone names known to this library.
/// </summary>
public static IReadOnlyCollection<string> KnownIanaCanonicalNames { get; }

/// <summary>
/// Gets a dictionary that has an sorted collection of IANA time zone names keyed by territory code.
/// </summary>
Expand Down Expand Up @@ -410,6 +418,32 @@ public static bool TryGetTimeZoneInfo(string windowsOrIanaTimeZoneId,
return false;
}

/// <summary>
/// Attempts to resolve the IANA canonical name given an IANA time zone name.
/// </summary>
/// <param name="ianaTimeZoneName">The IANA time zone name to resolve.</param>
/// <param name="ianaCanonicalName">The resolved IANA canonical time zone name.</param>
/// <returns><c>true</c> if successful, <c>false</c> otherwise.</returns>
/// <remarks>
/// The resolving will succeed whether the input name is an alias or a canonical name.
/// </remarks>
public static bool TryGetIanaCanonicalName(
string ianaTimeZoneName,
[MaybeNullWhen(false)] out string ianaCanonicalName)
{
if (ianaTimeZoneName == null || !KnownIanaTimeZoneNames.Contains(ianaTimeZoneName))
{
ianaCanonicalName = null;
return false;
}

ianaCanonicalName = Links.TryGetValue(ianaTimeZoneName, out ianaCanonicalName)
? ianaCanonicalName
: ianaTimeZoneName;

return true;
}

/// <summary>
/// Converts an IANA time zone name to one or more equivalent Rails time zone names.
/// </summary>
Expand Down
62 changes: 62 additions & 0 deletions test/TimeZoneConverter.Tests/TimeZoneInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,66 @@ public void CanGetArgentinaStandardTime()
var tz = TZConvert.GetTimeZoneInfo("Argentina Standard Time");
Assert.NotNull(tz);
}

[Fact]
public void CanGetIanaCanonicalNameForAlias()
{
var result = TZConvert.TryGetIanaCanonicalName("Europe/Jersey", out var canonicalIanaName);

Assert.True(result, $"{nameof(TZConvert.TryGetIanaCanonicalName)} should succeed for alias.");
Assert.Equal("Europe/London", canonicalIanaName);
}

[Fact]
public void CanGetIanaCanonicalNameForCanonicalName()
{
var result = TZConvert.TryGetIanaCanonicalName("Africa/Maputo", out var canonicalIanaName);

Assert.True(result, $"{nameof(TZConvert.TryGetIanaCanonicalName)} should succeed for canonical time zone name.");
Assert.Equal("Africa/Maputo", canonicalIanaName);
}

[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
[InlineData("NOT-A-TIME-ZONE")]
public void CannotGetIanaCanonicalNameForUnknownOrInvalidName(string? name)
{
var result = TZConvert.TryGetIanaCanonicalName(name, out _);

Assert.False(result, $"{nameof(TZConvert.TryGetIanaCanonicalName)} should fail for invalid or unknown time zone name.");
}

[Fact]
public void CanGetIanaCanonicalNameForAllKnownIanaTimeZones()
{
var names = TZConvert.KnownIanaTimeZoneNames.ToList();

foreach (var name in names)
{
var result = TZConvert.TryGetIanaCanonicalName(name, out _);

Assert.True(result, $"{nameof(TZConvert.TryGetIanaCanonicalName)} should succeed for \"{name}\".");
}
}

[Theory]
[InlineData("Australia/Brisbane")]
[InlineData("Antarctica/Troll")]
[InlineData("America/Adak")]
public void CanFindCanonicalTimeZoneInCanonicalNames(string name)
{
var result = TZConvert.KnownIanaCanonicalNames.Contains(name);

Assert.True(result, $"{nameof(TZConvert.KnownIanaCanonicalNames)} should contain \"{name}\".");
}

[Fact]
public void CannotFindAliasInCanonicalNames()
{
var result = TZConvert.KnownIanaCanonicalNames.Contains("Australia/Queensland");

Assert.False(result, $"{nameof(TZConvert.KnownIanaCanonicalNames)} should not contain \"Australia/Queensland\".");
}
}