Skip to content

Commit

Permalink
Enhance time zone handling and add unit tests
Browse files Browse the repository at this point in the history
* Updated the `IZonedTimeInfo` and `ZonedTime` classes to clarify the documentation for the `BaseUtcOffset` property, specifying its relation to UTC.
* Added a new test class `TimeZoneConverterTests` with multiple test methods to verify various functionalities of the `TimeZoneConverter`, including conversions between UTC and time zones, handling of `DateTime` kinds, and mapping to IANA time zones.
  • Loading branch information
axunonb committed Oct 1, 2024
1 parent c3f3835 commit b6dced7
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 4 deletions.
202 changes: 202 additions & 0 deletions Axuno.Tools.Tests/DateAndTime/TimeZoneConverterTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
using System.Globalization;
using NUnit.Framework;
using NodaTime;

namespace Axuno.Tools.Tests.DateAndTime;

[TestFixture]
public class TimeZoneConverterTests
{
private static Axuno.Tools.DateAndTime.TimeZoneConverter GetTimeZoneConverter(string culture)
{
var tzc = new Axuno.Tools.DateAndTime.TimeZoneConverter(
new NodaTime.TimeZones.DateTimeZoneCache(NodaTime.TimeZones.TzdbDateTimeZoneSource.Default),
"Europe/Berlin",
CultureInfo.GetCultureInfo(culture),
NodaTime.TimeZones.Resolvers.LenientResolver);
return tzc;
}

[Test]
public void ConvertUtcToTimeZoneStandard_ShouldReturnCorrectDateTime()
{
// Arrange
var utcDateTime = new DateTime(2022, 1, 1, 12, 0, 0, DateTimeKind.Utc);
var expectedDateTime = new DateTime(2022, 1, 1, 13, 0, 0, DateTimeKind.Local);

// Act
var convertedDateTime = GetTimeZoneConverter("de-DE").ToZonedTime(utcDateTime)!;

// Assert
Assert.Multiple(() =>
{
Assert.That(convertedDateTime.DateTimeOffset.DateTime, Is.EqualTo(expectedDateTime));
Assert.That(convertedDateTime.TimeZoneId, Is.EqualTo("Europe/Berlin"));
Assert.That(convertedDateTime.CultureInfo.TwoLetterISOLanguageName, Is.EqualTo("de"));
Assert.That(convertedDateTime.GenericName, Is.EqualTo("Mitteleuropäische Zeit"));
Assert.That(convertedDateTime.GenericAbbreviation, Is.EqualTo("MEZ"));
Assert.That(convertedDateTime.DisplayName, Is.EqualTo("(UTC+01:00) Amsterdam, Berlin, Bern, Rom, Stockholm, Wien"));
Assert.That(convertedDateTime.Name, Is.EqualTo("Mitteleuropäische Normalzeit"));
Assert.That(convertedDateTime.Abbreviation, Is.EqualTo("MEZ"));
Assert.That(convertedDateTime.IsDaylightSavingTime, Is.False);
Assert.That(convertedDateTime.DateTimeOffset.Offset, Is.EqualTo(new TimeSpan(0, 1, 0, 0)));
Assert.That(convertedDateTime.BaseUtcOffset, Is.EqualTo(new TimeSpan(0,1,0,0)));
});
}

[Test]
public void ConvertUtcToTimeZoneDaylight_ShouldReturnCorrectDateTime()
{
// Arrange
var utcDateTime = new DateTime(2022, 7, 1, 12, 0, 0, DateTimeKind.Utc);
var expectedDateTime = new DateTime(2022, 7, 1, 14, 0, 0, DateTimeKind.Local);

// Act
var convertedDateTime = GetTimeZoneConverter("de-DE").ToZonedTime(utcDateTime)!;

// Assert
Assert.Multiple(() =>
{
Assert.That(convertedDateTime.DateTimeOffset.DateTime, Is.EqualTo(expectedDateTime));
Assert.That(convertedDateTime.Name, Is.EqualTo("Mitteleuropäische Sommerzeit"));
Assert.That(convertedDateTime.Abbreviation, Is.EqualTo("MESZ"));
Assert.That(convertedDateTime.IsDaylightSavingTime, Is.True);
Assert.That(convertedDateTime.DateTimeOffset.Offset, Is.EqualTo(new TimeSpan(0, 2, 0, 0)));
Assert.That(convertedDateTime.BaseUtcOffset, Is.EqualTo(new TimeSpan(0, 1, 0, 0)));
});
}

[Test]
public void ConvertUnspecifiedKindToTimeZone_ShouldReturnCorrectDateTime()
{
// Arrange
var unspecifiedDateTime = new DateTime(2022, 1, 1, 12, 0, 0, DateTimeKind.Unspecified);
var expectedDateTime = new DateTime(2022, 1, 1, 13, 0, 0, DateTimeKind.Local);

// Act
var convertedDateTime = GetTimeZoneConverter("de-DE").ToZonedTime(unspecifiedDateTime)!;

// Assert
Assert.That(convertedDateTime.DateTimeOffset.DateTime, Is.EqualTo(expectedDateTime));
}

[Test]
public void ConvertKindLocalToTimeZone_ShouldReturnCorrectDateTime()
{
// Arrange
var localDateTime = new DateTime(2022, 1, 1, 12, 0, 0, DateTimeKind.Local);
var expectedDateTime = new DateTime(2022, 1, 1, 12, 0, 0, DateTimeKind.Local);

// Act
var convertedDateTime = GetTimeZoneConverter("de-DE").ToZonedTime(localDateTime)!;

// Assert
Assert.That(convertedDateTime.DateTimeOffset.DateTime, Is.EqualTo(expectedDateTime));
}

[Test]
public void ConvertTimeZoneToUtc_ShouldReturnCorrectDateTime()
{
// Arrange
var localDateTime = new DateTime(2022, 1, 1, 8, 0, 0, DateTimeKind.Local);
var expectedDateTime = new DateTime(2022, 1, 1, 7, 0, 0, DateTimeKind.Utc);

// Act
var convertedDateTime = GetTimeZoneConverter("en-US").ToUtc(localDateTime);

// Assert
Assert.That(convertedDateTime, Is.EqualTo(expectedDateTime));
}

[Test]
public void ConvertTimeWithZoneIdToUtc_ShouldReturnCorrectDateTime()
{
// Arrange
var localDateTime = new DateTime(2022, 1, 1, 8, 0, 0, DateTimeKind.Local);
var expectedDateTime = new DateTime(2022, 1, 1, 7, 0, 0, DateTimeKind.Utc);

// Act
var convertedDateTime = GetTimeZoneConverter("en-US").ToUtc(localDateTime, "Europe/Berlin");

// Assert
Assert.That(convertedDateTime, Is.EqualTo(expectedDateTime));
}

[Test]
public void ConvertUtcToTimeZone_ShouldReturnNull_WhenUtcDateTimeIsNull()
{
// Arrange
DateTime? utcDateTime = null;

// Act
var convertedDateTime = GetTimeZoneConverter("de-DE").ToZonedTime(utcDateTime);

// Assert
Assert.That(convertedDateTime, Is.Null);
}

[Test]
public void ConvertTimeZoneToUtc_ShouldReturnNull_WhenLocalDateTimeIsNull()
{
// Arrange
DateTime? localDateTime = null;

// Act
var convertedDateTime = GetTimeZoneConverter("en-US").ToUtc(localDateTime);

// Assert
Assert.That(convertedDateTime, Is.Null);
}

[Test]
public void ConvertUtcToTimeZone_ShouldReturnNull_WhenDateTimeOfAnyKindIsNull()
{
// Arrange
DateTime? dateTimeOfAnyKind = null;

// Act
var convertedDateTime = GetTimeZoneConverter("en-US").ToZonedTime(dateTimeOfAnyKind);

// Assert
Assert.That(convertedDateTime, Is.Null);
}

[Test]
public void ConvertTimeZoneToUtc_ShouldReturnNull_WhenDateTimeOfAnyKindIsNull()
{
// Arrange
DateTime? dateTimeOfAnyKind = null;

// Act
var convertedDateTime = GetTimeZoneConverter("en-US").ToUtc(dateTimeOfAnyKind, "Europe/Berlin");

// Assert
Assert.That(convertedDateTime, Is.Null);
}
[Test]
public void GetTimeZoneList_ShouldReturnTimeZoneList_WhenTimeZoneProviderIsNull()
{
// Arrange
IDateTimeZoneProvider? timeZoneProvider = null;

// Act
var timeZoneList = Axuno.Tools.DateAndTime.TimeZoneConverter.GetTimeZoneList(timeZoneProvider);

// Assert
Assert.That(timeZoneList, Is.InstanceOf<IReadOnlyCollection<string>>());
}

[Test]
public void CanMapToIanaTimeZone_ShouldReturnTrue_WhenTimeZoneInfoIsValid()
{
// Arrange
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");

// Act
var canMap = Axuno.Tools.DateAndTime.TimeZoneConverter.CanMapToIanaTimeZone(timeZoneInfo);

// Assert
Assert.That(canMap, Is.True);
}
}

2 changes: 1 addition & 1 deletion Axuno.Tools/DateAndTime/IZonedTimeInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public interface IZonedTimeInfo
bool IsDaylightSavingTime { get; }

/// <summary>
/// Gets the base UTC offset of the timezone related to the <see cref="DateTimeOffset"/>.
/// Gets the time difference between the current time zone's standard time and Coordinated Universal Time (UTC).
/// </summary>
TimeSpan BaseUtcOffset { get; }
}
4 changes: 2 additions & 2 deletions Axuno.Tools/DateAndTime/TimeZoneConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public TimeZoneConverter(IDateTimeZoneProvider dateTimeZoneProvider, string iana
/// <summary>
/// Converts the <see cref="DateTime"/> of any <see cref="DateTimeKind"/> to a <see cref="DateTime"/> of <see cref="DateTimeKind.Utc"/>.
/// </summary>
/// <param name="zoneDateTime">A <see cref="DateTime"/> in the the timezone specified with the timezone ID given when creating this converter instance.</param>
/// <param name="zoneDateTime">A <see cref="DateTime"/> in the timezone specified with the timezone ID given when creating this converter instance.</param>
/// <returns>Returns the converted <see cref="DateTime"/> with <see cref="DateTimeKind.Utc"/> or null, if the <paramref name="zoneDateTime"/> parameter is null.</returns>
public DateTime? ToUtc(DateTime? zoneDateTime)
{
Expand All @@ -83,7 +83,7 @@ public TimeZoneConverter(IDateTimeZoneProvider dateTimeZoneProvider, string iana
/// <summary>
/// Converts the <see cref="DateTime"/> of any <see cref="DateTimeKind"/> to a <see cref="DateTime"/> of <see cref="DateTimeKind.Utc"/>.
/// </summary>
/// <param name="zoneDateTime">A <see cref="DateTime"/> in the the timezone specified with the timezone ID given when creating this converter instance.</param>
/// <param name="zoneDateTime">A <see cref="DateTime"/> in the timezone specified with the timezone ID given when creating this converter instance.</param>
/// <returns>Returns the converted <see cref="DateTime"/> with <see cref="DateTimeKind.Utc"/>.</returns>
public DateTime ToUtc(DateTime zoneDateTime)
{
Expand Down
2 changes: 1 addition & 1 deletion Axuno.Tools/DateAndTime/ZonedTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ internal ZonedTime()
public bool IsDaylightSavingTime { get; internal set; }

/// <summary>
/// Gets the base UTC offset of the timezone related to the <see cref="DateTimeOffset"/>.
/// Gets the time difference between the current time zone's standard time and Coordinated Universal Time (UTC).
/// </summary>
public TimeSpan BaseUtcOffset { get; internal set; }
}

0 comments on commit b6dced7

Please sign in to comment.