diff --git a/Axuno.Tools.Tests/DateAndTime/TimeZoneConverterTests.cs b/Axuno.Tools.Tests/DateAndTime/TimeZoneConverterTests.cs index 24e9436..93192cb 100644 --- a/Axuno.Tools.Tests/DateAndTime/TimeZoneConverterTests.cs +++ b/Axuno.Tools.Tests/DateAndTime/TimeZoneConverterTests.cs @@ -1,6 +1,5 @@ using System.Globalization; using NUnit.Framework; -using NodaTime; namespace Axuno.Tools.Tests.DateAndTime; @@ -9,14 +8,33 @@ 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); + var tzc = new Axuno.Tools.DateAndTime.TimeZoneConverter("Europe/Berlin", CultureInfo.GetCultureInfo(culture)); return tzc; } + [Test] + public void UnknownTimeZoneId_ShouldThrow() + { + // Act, Assert + Assert.That(() => + { + _ = new Axuno.Tools.DateAndTime.TimeZoneConverter("unknown-time-zone", CultureInfo.CurrentCulture); + }, Throws.Exception.TypeOf()); + } + + [Test] + public void UseCurrentCulture_IfCultureIsMissing() + { + // Arrange + var utcDateTime = new DateTime(2022, 1, 1, 12, 0, 0, DateTimeKind.Utc); + + // Act + var convertedDateTime = new Axuno.Tools.DateAndTime.TimeZoneConverter("Europe/Berlin").ToZonedTime(utcDateTime)!; + + // Assert + Assert.That(convertedDateTime.CultureInfo.TwoLetterISOLanguageName, Is.EqualTo(CultureInfo.CurrentUICulture.TwoLetterISOLanguageName)); + } + [Test] public void ConvertUtcToTimeZoneStandard_ShouldReturnCorrectDateTime() { @@ -40,7 +58,7 @@ public void ConvertUtcToTimeZoneStandard_ShouldReturnCorrectDateTime() 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))); + Assert.That(convertedDateTime.BaseUtcOffset, Is.EqualTo(new TimeSpan(0, 1, 0, 0))); }); } @@ -116,7 +134,7 @@ public void ConvertTimeWithZoneIdToUtc_ShouldReturnCorrectDateTime() var expectedDateTime = new DateTime(2022, 1, 1, 7, 0, 0, DateTimeKind.Utc); // Act - var convertedDateTime = GetTimeZoneConverter("en-US").ToUtc(localDateTime, "Europe/Berlin"); + var convertedDateTime = Axuno.Tools.DateAndTime.TimeZoneConverter.ToUtc(localDateTime, "Europe/Berlin"); // Assert Assert.That(convertedDateTime, Is.EqualTo(expectedDateTime)); @@ -168,19 +186,27 @@ public void ConvertTimeZoneToUtc_ShouldReturnNull_WhenDateTimeOfAnyKindIsNull() DateTime? dateTimeOfAnyKind = null; // Act - var convertedDateTime = GetTimeZoneConverter("en-US").ToUtc(dateTimeOfAnyKind, "Europe/Berlin"); + var convertedDateTime = Axuno.Tools.DateAndTime.TimeZoneConverter.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.GetSystemTimeZoneList(); + // Assert + Assert.That(timeZoneList, Is.InstanceOf>()); + } + + [Test] + public void GetIanaTimeZoneList_ShouldReturnTimeZoneList_WhenTimeZoneProviderIsNull() + { // Act - var timeZoneList = Axuno.Tools.DateAndTime.TimeZoneConverter.GetTimeZoneList(timeZoneProvider); + var timeZoneList = Axuno.Tools.DateAndTime.TimeZoneConverter.GetIanaTimeZoneList(); // Assert Assert.That(timeZoneList, Is.InstanceOf>()); diff --git a/Axuno.Tools/Axuno.Tools.csproj b/Axuno.Tools/Axuno.Tools.csproj index d14962f..1bd4937 100644 --- a/Axuno.Tools/Axuno.Tools.csproj +++ b/Axuno.Tools/Axuno.Tools.csproj @@ -9,7 +9,6 @@ enable - all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Axuno.Tools/DateAndTime/IZonedTimeInfo.cs b/Axuno.Tools/DateAndTime/IZonedTimeInfo.cs index a4097f9..ed95a54 100644 --- a/Axuno.Tools/DateAndTime/IZonedTimeInfo.cs +++ b/Axuno.Tools/DateAndTime/IZonedTimeInfo.cs @@ -8,15 +8,20 @@ namespace Axuno.Tools.DateAndTime; public interface IZonedTimeInfo { /// - /// Gets the used for localization. + /// Gets the used for localization. /// CultureInfo CultureInfo { get; } /// - /// Gets the IANA timezone ID related to the . + /// Gets the IANA timezone ID related to the . /// string TimeZoneId { get; } + /// + /// Gets the which is set based on the timezone offset to UTC. + /// + DateTimeOffset DateTimeOffset { get; } + /// /// Gets the generic name for the time zone. /// @@ -33,17 +38,17 @@ public interface IZonedTimeInfo string DisplayName { get; } /// - /// Gets the name of the timezone related to the . + /// Gets the name of the timezone related to the . /// string Name { get; } /// - /// Gets the timezone abbreviation related to the . + /// Gets the timezone abbreviation related to the . /// string Abbreviation { get; } /// - /// Gets whether the timezone related to the is daylight saving time. + /// Gets whether the timezone related to the is daylight saving time. /// bool IsDaylightSavingTime { get; } diff --git a/Axuno.Tools/DateAndTime/TimeZoneConverter.cs b/Axuno.Tools/DateAndTime/TimeZoneConverter.cs index 54596da..d326ac8 100644 --- a/Axuno.Tools/DateAndTime/TimeZoneConverter.cs +++ b/Axuno.Tools/DateAndTime/TimeZoneConverter.cs @@ -1,49 +1,30 @@ using System.Globalization; -using NodaTime; -using NodaTime.TimeZones; -using TzConverter = TimeZoneConverter; -namespace Axuno.Tools.DateAndTime; +using TimeZoneNames; +namespace Axuno.Tools.DateAndTime; /// -/// Converts from or and zone specific . +/// Converts between or and zone specific . /// -/// -/// There is a NuGet package "TimeZoneConverter", version=6.1.0+ which might be able to replace this class. -/// Credits to Joe Audette's blog who introduced a similar solution. Unfortunately Joe passed away in 2020. -/// public class TimeZoneConverter { - private readonly IDateTimeZoneProvider _dateTimeZoneProvider; - private readonly string _timeZoneId; - private readonly CultureInfo? _cultureInfo; - private readonly ZoneLocalMappingResolver? _resolver; - - /// - /// CTOR. - /// - /// - /// The Windows to use for converting. - /// The to use for converting. - /// The to use for converting. - public TimeZoneConverter(IDateTimeZoneProvider dateTimeZoneProvider, TimeZoneInfo timeZoneInfo, - CultureInfo? cultureInfo = null, ZoneLocalMappingResolver? resolver = null) : this(dateTimeZoneProvider, - TzConverter.TZConvert.WindowsToIana(timeZoneInfo.Id), cultureInfo, resolver) - {} + // IANA timezone ID + private readonly string _ianaTimeZoneId; + private readonly CultureInfo _cultureInfo; /// /// CTOR. /// - /// - /// The IANA timezone ID to use for converting. - /// The to use for converting. - /// The to use for converting. - public TimeZoneConverter(IDateTimeZoneProvider dateTimeZoneProvider, string ianaTimeZoneId, - CultureInfo? cultureInfo = null, ZoneLocalMappingResolver? resolver = null) + /// + /// The IANA timezone ID to use for converting. + /// We initialize with IANA because of compatibility with the NodaTime TimeZoneConverter we had before. + /// + /// The to use for converting. Default is . + /// + public TimeZoneConverter(string ianaTimeZoneId, CultureInfo? cultureInfo = null) { - _dateTimeZoneProvider = dateTimeZoneProvider; - _timeZoneId = ianaTimeZoneId; - _cultureInfo = cultureInfo; - _resolver = resolver; + _ianaTimeZoneId = ianaTimeZoneId; + _ = TimeZoneInfo.FindSystemTimeZoneById(ianaTimeZoneId); + _cultureInfo = cultureInfo ?? CultureInfo.CurrentUICulture; } /// @@ -53,9 +34,9 @@ public TimeZoneConverter(IDateTimeZoneProvider dateTimeZoneProvider, string iana /// /// The to use for time zone localization. If , the default culture will be used. /// Returns the converted as a instance or null, if the parameter is null. - public ZonedTime? ToZonedTime(DateTime? dateTimeOfAnyKind, CultureInfo? cultureInfo = null) + public IZonedTimeInfo? ToZonedTime(DateTime? dateTimeOfAnyKind, CultureInfo? cultureInfo = null) { - return ToZonedTime(dateTimeOfAnyKind, _timeZoneId, cultureInfo ?? _cultureInfo, _dateTimeZoneProvider); + return ToZonedTime(dateTimeOfAnyKind, _ianaTimeZoneId, cultureInfo ?? _cultureInfo); } /// @@ -65,9 +46,9 @@ public TimeZoneConverter(IDateTimeZoneProvider dateTimeZoneProvider, string iana /// /// The to use for time zone localization. If , the default culture will be used. /// Returns the converted as a instance. - public ZonedTime? ToZonedTime(DateTime dateTimeOfAnyKind, CultureInfo? cultureInfo = null) + public IZonedTimeInfo? ToZonedTime(DateTime dateTimeOfAnyKind, CultureInfo? cultureInfo = null) { - return ToZonedTime(dateTimeOfAnyKind, _timeZoneId, cultureInfo ?? _cultureInfo, _dateTimeZoneProvider); + return ToZonedTime(dateTimeOfAnyKind, _ianaTimeZoneId, cultureInfo ?? _cultureInfo); } /// @@ -77,7 +58,7 @@ public TimeZoneConverter(IDateTimeZoneProvider dateTimeZoneProvider, string iana /// Returns the converted with or null, if the parameter is null. public DateTime? ToUtc(DateTime? zoneDateTime) { - return ToUtc(zoneDateTime, _timeZoneId, _dateTimeZoneProvider, _resolver); + return ToUtc(zoneDateTime, _ianaTimeZoneId); } /// @@ -87,19 +68,23 @@ public TimeZoneConverter(IDateTimeZoneProvider dateTimeZoneProvider, string iana /// Returns the converted with . public DateTime ToUtc(DateTime zoneDateTime) { - return ToUtc(zoneDateTime, _timeZoneId, _dateTimeZoneProvider, _resolver); + return ToUtc(zoneDateTime, _ianaTimeZoneId); } - /// - /// Converts the of any to a of . - /// - /// A in the timezone specified with parameter . - /// The ID of the IANA timezone database, https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. - /// Returns the converted with or null, if the parameter is . - /// If is unknown. - public DateTime? ToUtc(DateTime? zoneDateTime, string timeZoneId) + public static DateTime? ToUtc(DateTime? zoneDateTime, string timeZoneId) { - return ToUtc(zoneDateTime, timeZoneId, _dateTimeZoneProvider, _resolver); + if (!zoneDateTime.HasValue) return null; + + // Convert IANA time zone to Windows time zone ID + var windowsTimeZoneId = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId); + + // Convert the local time to UTC + // We have to change the DateTimeKind to Unspecified, because the TimeZoneInfo.ConvertTimeToUtc method does not work with Local time + // This makes the method compatible with the NodaTime TimeZoneConverter we had before. + zoneDateTime = DateTime.SpecifyKind(zoneDateTime.Value, DateTimeKind.Unspecified); + var utcDateTime = TimeZoneInfo.ConvertTimeToUtc(zoneDateTime.Value, windowsTimeZoneId); + + return utcDateTime; } /// @@ -108,10 +93,10 @@ public DateTime ToUtc(DateTime zoneDateTime) /// A in the timezone specified with parameter . /// The ID of the IANA timezone database, https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. /// Returns the converted with . - /// If is unknown. - public DateTime ToUtc(DateTime zoneDateTime, string timeZoneId) + /// If is unknown. + public static DateTime ToUtc(DateTime zoneDateTime, string timeZoneId) { - return ToUtc(zoneDateTime, timeZoneId, _dateTimeZoneProvider, _resolver); + return (DateTime) ToUtc((DateTime?) zoneDateTime, timeZoneId)!; } /// @@ -121,25 +106,20 @@ public DateTime ToUtc(DateTime zoneDateTime, string timeZoneId) /// /// /// The see to use for localizing timezone strings. Default is . - /// The to use. For performance use a . - /// - /// IDateTimeZoneProvider dtzp = new DateTimeZoneCache(TzdbDateTimeZoneSource.Default) - /// - /// /// Returns the converted as a instance or null, if the parameter is null. - /// If is unknown. - public static ZonedTime? ToZonedTime(DateTime? dateTimeOfAnyKind, string timeZoneId, - CultureInfo? cultureInfo = null, IDateTimeZoneProvider? timeZoneProvider = null) + /// If is unknown. + public static IZonedTimeInfo? ToZonedTime(DateTime? dateTimeOfAnyKind, string timeZoneId, CultureInfo? cultureInfo = null) { if (!dateTimeOfAnyKind.HasValue) return null; - var utcDateTime = dateTimeOfAnyKind.Value.Kind switch { + var utcDateTime = dateTimeOfAnyKind.Value.Kind switch + { DateTimeKind.Utc => dateTimeOfAnyKind.Value, - DateTimeKind.Local => dateTimeOfAnyKind.Value.ToUniversalTime(), + DateTimeKind.Local => TimeZoneInfo.ConvertTimeToUtc(dateTimeOfAnyKind.Value), _ => DateTime.SpecifyKind(dateTimeOfAnyKind.Value, DateTimeKind.Utc) }; - return ToZonedTime(new DateTimeOffset(utcDateTime), timeZoneId, cultureInfo, timeZoneProvider); + return ToZonedTime(new DateTimeOffset(utcDateTime), timeZoneId, cultureInfo); } /// @@ -149,17 +129,11 @@ public DateTime ToUtc(DateTime zoneDateTime, string timeZoneId) /// /// /// The see to use for localizing timezone strings. Default is . - /// The to use. For performance use a . - /// - /// IDateTimeZoneProvider dtzp = new DateTimeZoneCache(TzdbDateTimeZoneSource.Default) - /// - /// /// Returns the converted as a instance. - /// If is unknown. - public static ZonedTime? ToZonedTime(DateTime dateTimeOfAnyKind, string timeZoneId, - CultureInfo? cultureInfo = null, IDateTimeZoneProvider? timeZoneProvider = null) + /// If is unknown. + public static IZonedTimeInfo? ToZonedTime(DateTime dateTimeOfAnyKind, string timeZoneId, CultureInfo? cultureInfo = null) { - return ToZonedTime((DateTime?) dateTimeOfAnyKind, timeZoneId, cultureInfo, timeZoneProvider); + return ToZonedTime((DateTime?) dateTimeOfAnyKind, timeZoneId, cultureInfo); } /// @@ -168,44 +142,34 @@ public DateTime ToUtc(DateTime zoneDateTime, string timeZoneId) /// /// The ID of the IANA timezone database, https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. /// The see to use for localizing timezone strings. Default is . - /// The to use. For performance use a . - /// - /// IDateTimeZoneProvider dtzp = new DateTimeZoneCache(TzdbDateTimeZoneSource.Default) - /// - /// /// Returns the converted as a instance or null, if the parameter is null. - /// If IANA is unknown. - public static ZonedTime? ToZonedTime(DateTimeOffset? dateTimeOffset, string timeZoneId, - CultureInfo? cultureInfo = null, IDateTimeZoneProvider? timeZoneProvider = null) + /// If IANA is unknown. + public static IZonedTimeInfo? ToZonedTime(DateTimeOffset? dateTimeOffset, string timeZoneId, CultureInfo? cultureInfo = null) { if (!dateTimeOffset.HasValue) return null; var zonedDateTime = new ZonedTime(); - timeZoneProvider ??= DateTimeZoneProviders.Tzdb; cultureInfo ??= CultureInfo.CurrentUICulture; - // throws if timeZoneId is unknown - var timeZone = timeZoneProvider[timeZoneId]; + // Convert IANA time zone to Windows time zone ID + var windowsTimeZoneId = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId); - var instantInZone = Instant.FromDateTimeUtc(dateTimeOffset.Value.UtcDateTime).InZone(timeZone); - zonedDateTime.CultureInfo = cultureInfo; - zonedDateTime.DateTimeOffset = instantInZone.ToDateTimeOffset(); - zonedDateTime.IsDaylightSavingTime = instantInZone.IsDaylightSavingTime(); + // Convert UTC to the specified time zone + var localDateTime = TimeZoneInfo.ConvertTime(dateTimeOffset.Value.UtcDateTime, windowsTimeZoneId); - var timeZoneInfo = TzConverter.TZConvert.GetTimeZoneInfo(timeZoneId); - zonedDateTime.DisplayName = timeZoneInfo.DisplayName; - zonedDateTime.BaseUtcOffset = timeZoneInfo.BaseUtcOffset; + zonedDateTime.CultureInfo = cultureInfo; + zonedDateTime.DateTimeOffset = new DateTimeOffset(localDateTime, windowsTimeZoneId.GetUtcOffset(localDateTime)); + zonedDateTime.IsDaylightSavingTime = windowsTimeZoneId.IsDaylightSavingTime(localDateTime); + zonedDateTime.DisplayName = windowsTimeZoneId.DisplayName; + zonedDateTime.BaseUtcOffset = windowsTimeZoneId.BaseUtcOffset; zonedDateTime.TimeZoneId = timeZoneId; - var tzNames = - TimeZoneNames.TZNames.GetNamesForTimeZone(timeZone.Id, cultureInfo.TwoLetterISOLanguageName); + var tzNames = TZNames.GetNamesForTimeZone(timeZoneId, cultureInfo.TwoLetterISOLanguageName); zonedDateTime.GenericName = tzNames.Generic ?? string.Empty; zonedDateTime.Name = (zonedDateTime.IsDaylightSavingTime ? tzNames.Daylight : tzNames.Standard) ?? string.Empty; - var tzAbbr = - TimeZoneNames.TZNames.GetAbbreviationsForTimeZone(timeZone.Id, - cultureInfo.TwoLetterISOLanguageName); + var tzAbbr = TZNames.GetAbbreviationsForTimeZone(timeZoneId, cultureInfo.TwoLetterISOLanguageName); zonedDateTime.GenericAbbreviation = tzAbbr.Generic ?? string.Empty; zonedDateTime.Abbreviation = (zonedDateTime.IsDaylightSavingTime ? tzAbbr.Daylight : tzAbbr.Standard) ?? string.Empty; @@ -218,77 +182,48 @@ public DateTime ToUtc(DateTime zoneDateTime, string timeZoneId) /// /// The ID of the IANA timezone database, https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. /// The see to use for localizing timezone strings. Default is . - /// The to use. For performance use a . - /// - /// IDateTimeZoneProvider dtzp = new DateTimeZoneCache(TzdbDateTimeZoneSource.Default) - /// - /// /// Returns the converted as a instance. - /// If IANA is unknown. - public static ZonedTime? ToZonedTime(DateTimeOffset dateTimeOffset, string timeZoneId, - CultureInfo? cultureInfo = null, IDateTimeZoneProvider? timeZoneProvider = null) + /// If IANA is unknown. + public static IZonedTimeInfo? ToZonedTime(DateTimeOffset dateTimeOffset, string timeZoneId, CultureInfo? cultureInfo = null) { - return ToZonedTime((DateTimeOffset?) dateTimeOffset, timeZoneId, cultureInfo, timeZoneProvider); + return ToZonedTime((DateTimeOffset?) dateTimeOffset, timeZoneId, cultureInfo); } /// - /// Converts the of any to a of . + /// Checks whether the Windows can be mapped to a IANA timezone ID. /// - /// - /// The ID of the IANA timezone database, https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. - /// - /// The to use. Default is ´, which never throws an exception due to ambiguity or skipped time. - /// Returns the converted with or null, if the parameter is null. - /// If is unknown. - public static DateTime? ToUtc(DateTime? zoneDateTime, string timeZoneId, - IDateTimeZoneProvider? timeZoneProvider, ZoneLocalMappingResolver? resolver = null) + /// + /// Returns true if the can be mapped to a IANA timezone, otherwise false. + public static bool CanMapToIanaTimeZone(TimeZoneInfo timeZoneInfo) { - if (!zoneDateTime.HasValue) return null; - - timeZoneProvider ??= DateTimeZoneProviders.Tzdb; - // never throws an exception due to ambiguity or skipped time: - resolver ??= Resolvers.LenientResolver; - // throws if timeZoneId is unknown - var timeZone = timeZoneProvider[timeZoneId]; - - var local = LocalDateTime.FromDateTime(zoneDateTime.Value); - var zonedTime = timeZone.ResolveLocal(local, resolver); - return zonedTime.ToDateTimeUtc(); + return TimeZoneInfo.TryConvertWindowsIdToIanaId(timeZoneInfo.Id, out _); } /// - /// Converts the of any to a of . - /// - /// - /// The ID of the IANA timezone database, https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. - /// - /// The to use. Default is ´, which never throws an exception due to ambiguity or skipped time. - /// Returns the converted with . - /// If is unknown. - public static DateTime ToUtc(DateTime zoneDateTime, string timeZoneId, - IDateTimeZoneProvider timeZoneProvider, ZoneLocalMappingResolver? resolver = null) - { - return ToUtc((DateTime?)zoneDateTime, timeZoneId, timeZoneProvider, resolver)!.Value; - } - - /// - /// Checks whether the Windows can be mapped to a IANA timezone ID. + /// Get a collection of available s. /// - /// - /// Returns true if the can be mapped to a IANA timezone, otherwise false. - public static bool CanMapToIanaTimeZone(TimeZoneInfo timeZoneInfo) + /// Returns a collection of available s. + public static IReadOnlyCollection GetSystemTimeZoneList() { - return TzConverter.TZConvert.TryWindowsToIana(timeZoneInfo.Id, out _); + return TimeZoneInfo.GetSystemTimeZones().Select(tz => tz.Id).ToList().AsReadOnly(); } /// - /// Get a collection of available . + /// Get a collection of IANA Ids for s that can be converted to IANA. /// - /// The to use. Default is . - /// Returns a collection of available . - public static IReadOnlyCollection GetTimeZoneList(IDateTimeZoneProvider? timeZoneProvider = null) + /// A collection of IANA Ids for s that can be converted to IANA. + public static IReadOnlyCollection GetIanaTimeZoneList() { - timeZoneProvider ??= DateTimeZoneProviders.Tzdb; - return timeZoneProvider.Ids; + var ianaTimeZones = new List(); + + foreach (var timeZone in TimeZoneInfo.GetSystemTimeZones()) + { + if (TimeZoneInfo.TryConvertWindowsIdToIanaId(timeZone.Id, out var ianaTimeZoneId)) + { + ianaTimeZones.Add(ianaTimeZoneId); + } + } + + return ianaTimeZones.AsReadOnly(); } } diff --git a/Axuno.Tools/DateAndTime/ZonedTime.cs b/Axuno.Tools/DateAndTime/ZonedTime.cs index 0a580f3..7636f06 100644 --- a/Axuno.Tools/DateAndTime/ZonedTime.cs +++ b/Axuno.Tools/DateAndTime/ZonedTime.cs @@ -11,24 +11,16 @@ internal ZonedTime() { } - /// - /// Gets the used for localization. - /// + /// public CultureInfo CultureInfo { get; internal set; } = CultureInfo.InvariantCulture; - /// - /// Gets the IANA timezone ID related to the . - /// + /// public string TimeZoneId { get; internal set; } = string.Empty; - /// - /// Gets the which is set based on the timezone offset to UTC. - /// + /// public DateTimeOffset DateTimeOffset { get; internal set; } - /// - /// Gets the generic name for the time zone. - /// + /// public string GenericName { get; internal set; } = string.Empty; /// @@ -41,23 +33,15 @@ internal ZonedTime() /// public string DisplayName { get; internal set; } = string.Empty; - /// - /// Gets the name of the timezone related to the . - /// + /// public string Name { get; internal set; } = string.Empty; - /// - /// Gets the timezone abbreviation related to the . - /// + /// public string Abbreviation { get; internal set; } = string.Empty; - /// - /// Gets whether the timezone related to the is daylight saving time. - /// + /// public bool IsDaylightSavingTime { get; internal set; } - /// - /// Gets the time difference between the current time zone's standard time and Coordinated Universal Time (UTC). - /// + /// public TimeSpan BaseUtcOffset { get; internal set; } } diff --git a/Directory.Build.props b/Directory.Build.props index ba4b6de..e1b5c52 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,8 +7,8 @@ Copyright 2011-$(CurrentYear) axuno gGmbH https://github.com/axuno/Volleyball-League true - 7.0.1 - 7.0.0 + 7.2.1 + 7.2.1 7.0.0.0 latest enable diff --git a/League.Tests/TestComponents/UnitTestHelpers.cs b/League.Tests/TestComponents/UnitTestHelpers.cs index eeaede3..ceb9178 100644 --- a/League.Tests/TestComponents/UnitTestHelpers.cs +++ b/League.Tests/TestComponents/UnitTestHelpers.cs @@ -115,9 +115,7 @@ public static ServiceProvider GetTextTemplatingServiceProvider(ITenantContext te }) .AddLocalization() .AddSingleton(sp => new Axuno.Tools.DateAndTime.TimeZoneConverter( - sp.GetRequiredService(), "Europe/Berlin", - CultureInfo.CurrentCulture, - NodaTime.TimeZones.Resolvers.LenientResolver)) + "Europe/Berlin", CultureInfo.CurrentCulture)) .AddTransient(sp => tenantContext) .AddTextTemplatingModule(vfs => { diff --git a/League/LeagueStartup.cs b/League/LeagueStartup.cs index 0c55041..541ad4d 100644 --- a/League/LeagueStartup.cs +++ b/League/LeagueStartup.cs @@ -452,14 +452,10 @@ public static void ConfigureServices(WebApplicationBuilder builder, ILoggerFacto #region ** Timezone service per request ** - services.AddSingleton(sp => - new NodaTime.TimeZones.DateTimeZoneCache(NodaTime.TimeZones.TzdbDateTimeZoneSource.Default)); - - var tzId = configuration.GetSection("TimeZone").Value ?? "America/New_York"; + var ianaTzId = configuration.GetSection("TimeZone").Value ?? "America/New_York"; // TimeZoneConverter will use the culture of the current scope services.AddScoped(sp => new Axuno.Tools.DateAndTime.TimeZoneConverter( - sp.GetRequiredService(), tzId, CultureInfo.CurrentCulture, - NodaTime.TimeZones.Resolvers.LenientResolver)); + ianaTzId, CultureInfo.CurrentCulture)); #endregion diff --git a/League/TextTemplatingModule/TextTemplatingServiceCollectionExtensions.cs b/League/TextTemplatingModule/TextTemplatingServiceCollectionExtensions.cs index ce01e6d..f0fe108 100644 --- a/League/TextTemplatingModule/TextTemplatingServiceCollectionExtensions.cs +++ b/League/TextTemplatingModule/TextTemplatingServiceCollectionExtensions.cs @@ -38,18 +38,14 @@ public static IServiceCollection AddTextTemplatingModule(this IServiceCollection services.TryAddSingleton(typeof(IStringLocalizer<>), typeof(StringLocalizer<>)); #region ** Timezone service ** - - services.TryAddSingleton(sp => - new NodaTime.TimeZones.DateTimeZoneCache(NodaTime.TimeZones.TzdbDateTimeZoneSource.Default)); - var tzId = "America/New_York"; // America/New_York + var ianaTzId = "America/New_York"; // America/New_York // TimeZoneConverter will use the culture of the current scope services.TryAddTransient(sp => new Axuno.Tools.DateAndTime.TimeZoneConverter( - sp.GetRequiredService(), tzId, CultureInfo.GetCultureInfo("en"), - NodaTime.TimeZones.Resolvers.LenientResolver)); + ianaTzId, CultureInfo.GetCultureInfo("en"))); #endregion return services; } -} \ No newline at end of file +} diff --git a/League/Views/Match/Results.cshtml b/League/Views/Match/Results.cshtml index a5ae50a..e9be126 100644 --- a/League/Views/Match/Results.cshtml +++ b/League/Views/Match/Results.cshtml @@ -81,7 +81,7 @@ const string unknown = "-"; var matches = Model.CompletedMatches.Where(m => m.RoundId == r.RoundId).OrderBy(m => m.RoundLegSequenceNo).ThenBy(m => m.MatchDate).ToList(); var legs = matches.GroupBy(m => m.RoundLegSequenceNo).ToList(); - Axuno.Tools.DateAndTime.ZonedTime? zonedTime; + Axuno.Tools.DateAndTime.IZonedTimeInfo? zonedTime; for (var i = 0; i < matches.Count; i++) { var match = matches[i]; diff --git a/TournamentManager/TournamentManager.Tests/Importers/ExcludeDates/ExcelImporterTests.cs b/TournamentManager/TournamentManager.Tests/Importers/ExcludeDates/ExcelImporterTests.cs index 20a2337..412cbe3 100644 --- a/TournamentManager/TournamentManager.Tests/Importers/ExcludeDates/ExcelImporterTests.cs +++ b/TournamentManager/TournamentManager.Tests/Importers/ExcludeDates/ExcelImporterTests.cs @@ -17,9 +17,7 @@ public void Import(DateTime from, DateTime to, int expectedCount) var xlFilePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Assets", "ExcludedDates.xlsx"); // using CET as time zone var tzConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter( - new NodaTime.TimeZones.DateTimeZoneCache(NodaTime.TimeZones.TzdbDateTimeZoneSource.Default), "Europe/Berlin", - CultureInfo.CurrentCulture, - NodaTime.TimeZones.Resolvers.LenientResolver); + "Europe/Berlin", CultureInfo.CurrentCulture); var xlImporter = new ExcelImporter(xlFilePath, tzConverter, NullLogger.Instance); var imported = xlImporter.Import(new DateTimePeriod(from, to)).ToList(); @@ -36,9 +34,7 @@ public void ImportShouldSwapFromTo(DateTime from, DateTime to) var xlFilePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Assets", "ExcludedDates.xlsx"); // Using UTC as time zone var tzConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter( - new NodaTime.TimeZones.DateTimeZoneCache(NodaTime.TimeZones.TzdbDateTimeZoneSource.Default), "UTC", - CultureInfo.CurrentCulture, - NodaTime.TimeZones.Resolvers.LenientResolver); + "UTC", CultureInfo.CurrentCulture); var xlImporter = new ExcelImporter(xlFilePath, tzConverter, NullLogger.Instance); var imported = xlImporter.Import(new DateTimePeriod(from, to)).ToList(); diff --git a/TournamentManager/TournamentManager.Tests/Importers/ExcludeDates/GermanHolidayImporterTests.cs b/TournamentManager/TournamentManager.Tests/Importers/ExcludeDates/GermanHolidayImporterTests.cs index 12156b5..3ad641e 100644 --- a/TournamentManager/TournamentManager.Tests/Importers/ExcludeDates/GermanHolidayImporterTests.cs +++ b/TournamentManager/TournamentManager.Tests/Importers/ExcludeDates/GermanHolidayImporterTests.cs @@ -16,9 +16,7 @@ public void Import_HolidaysInAllFederalStates(DateTime from, DateTime to, int ex { // using CET as time zone var tzConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter( - new NodaTime.TimeZones.DateTimeZoneCache(NodaTime.TimeZones.TzdbDateTimeZoneSource.Default), "Europe/Berlin", - CultureInfo.CurrentCulture, - NodaTime.TimeZones.Resolvers.LenientResolver); + "Europe/Berlin", CultureInfo.CurrentCulture); var holidayFilter = new Predicate(h => h.Type == GermanHolidays.Type.Public && h.PublicHolidayStateIds.Count == new GermanFederalStates().Count); var hImporter = new GermanHolidayImporter(null, holidayFilter, tzConverter, NullLogger.Instance); @@ -34,9 +32,7 @@ public void Import_HolidaysInBavaria(DateTime from, DateTime to, int expectedCou { // using CET as time zone var tzConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter( - new NodaTime.TimeZones.DateTimeZoneCache(NodaTime.TimeZones.TzdbDateTimeZoneSource.Default), "Europe/Berlin", - CultureInfo.CurrentCulture, - NodaTime.TimeZones.Resolvers.LenientResolver); + "Europe/Berlin", CultureInfo.CurrentCulture); var holidayFilter = new Predicate(h => h.Type == GermanHolidays.Type.Public && h.PublicHolidayStateIds.Contains(GermanFederalStates.Id.Bayern)); var hImporter = new GermanHolidayImporter(null, holidayFilter, tzConverter, NullLogger.Instance); @@ -52,9 +48,7 @@ public void Import_Holidays_Volleyball_League_Augsburg(DateTime from, DateTime t { // using CET as time zone var tzConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter( - new NodaTime.TimeZones.DateTimeZoneCache(NodaTime.TimeZones.TzdbDateTimeZoneSource.Default), "Europe/Berlin", - CultureInfo.CurrentCulture, - NodaTime.TimeZones.Resolvers.LenientResolver); + "Europe/Berlin", CultureInfo.CurrentCulture); var holidayFilter = new Predicate( h => h.Type == GermanHolidays.Type.Public && @@ -80,9 +74,7 @@ public void Import_With_Custom_School_Holidays(DateTime from, DateTime to, int e var customHolidayFilePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Assets", "Custom_Holidays_Sample.xml"); // using CET as time zone var tzConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter( - new NodaTime.TimeZones.DateTimeZoneCache(NodaTime.TimeZones.TzdbDateTimeZoneSource.Default), "Europe/Berlin", - CultureInfo.CurrentCulture, - NodaTime.TimeZones.Resolvers.LenientResolver); + "Europe/Berlin", CultureInfo.CurrentCulture); var holidayFilter = new Predicate(h => h.Type == GermanHolidays.Type.School); var hImporter = new GermanHolidayImporter(customHolidayFilePath, holidayFilter, tzConverter, NullLogger.Instance); diff --git a/TournamentManager/TournamentManager.Tests/Importers/ExcludeDates/InternetCalendarImporterTests.cs b/TournamentManager/TournamentManager.Tests/Importers/ExcludeDates/InternetCalendarImporterTests.cs index d68232f..660fee6 100644 --- a/TournamentManager/TournamentManager.Tests/Importers/ExcludeDates/InternetCalendarImporterTests.cs +++ b/TournamentManager/TournamentManager.Tests/Importers/ExcludeDates/InternetCalendarImporterTests.cs @@ -16,9 +16,7 @@ public void Import_InternetCalender_From_String(DateTime from, DateTime to, int var icsFilePath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Assets", "School_Holidays_Bavaria_2024.ics"); // using CET as time zone var tzConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter( - new NodaTime.TimeZones.DateTimeZoneCache(NodaTime.TimeZones.TzdbDateTimeZoneSource.Default), "Europe/Berlin", - CultureInfo.CurrentCulture, - NodaTime.TimeZones.Resolvers.LenientResolver); + "Europe/Berlin", CultureInfo.CurrentCulture); var icsImporter = new InternetCalendarImporter(File.ReadAllText(icsFilePath, Encoding.UTF8), "Europe/Berlin", NullLogger.Instance); diff --git a/TournamentManager/TournamentManager.Tests/ModelValidators/FixtureValidatorTests.cs b/TournamentManager/TournamentManager.Tests/ModelValidators/FixtureValidatorTests.cs index 6a05690..bd190b1 100644 --- a/TournamentManager/TournamentManager.Tests/ModelValidators/FixtureValidatorTests.cs +++ b/TournamentManager/TournamentManager.Tests/ModelValidators/FixtureValidatorTests.cs @@ -23,9 +23,7 @@ public FixtureValidatorTests() { #region *** TimeZoneConverter *** - var tzProvider = new NodaTime.TimeZones.DateTimeZoneCache(NodaTime.TimeZones.TzdbDateTimeZoneSource.Default); - var tzId = "Europe/Berlin"; - _data.TimeZoneConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter(tzProvider, tzId, _culture, NodaTime.TimeZones.Resolvers.LenientResolver); + _data.TimeZoneConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter("Europe/Berlin", _culture); #endregion diff --git a/TournamentManager/TournamentManager.Tests/ModelValidators/MatchResultValidatorTests.cs b/TournamentManager/TournamentManager.Tests/ModelValidators/MatchResultValidatorTests.cs index fdc7cdd..062a8a8 100644 --- a/TournamentManager/TournamentManager.Tests/ModelValidators/MatchResultValidatorTests.cs +++ b/TournamentManager/TournamentManager.Tests/ModelValidators/MatchResultValidatorTests.cs @@ -17,9 +17,7 @@ public MatchResultValidatorTests() { #region *** TimeZoneConverter *** - var tzProvider = new NodaTime.TimeZones.DateTimeZoneCache(NodaTime.TimeZones.TzdbDateTimeZoneSource.Default); - var tzId = "Europe/Berlin"; - _data.TimeZoneConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter(tzProvider, tzId, System.Globalization.CultureInfo.GetCultureInfo("en-US"), NodaTime.TimeZones.Resolvers.LenientResolver); + _data.TimeZoneConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter("Europe/Berlin", System.Globalization.CultureInfo.GetCultureInfo("en-US")); #endregion diff --git a/TournamentManager/TournamentManager.Tests/Plan/AvailableMatchDatesTests.cs b/TournamentManager/TournamentManager.Tests/Plan/AvailableMatchDatesTests.cs index 78d956a..9fad883 100644 --- a/TournamentManager/TournamentManager.Tests/Plan/AvailableMatchDatesTests.cs +++ b/TournamentManager/TournamentManager.Tests/Plan/AvailableMatchDatesTests.cs @@ -80,9 +80,7 @@ private static AvailableMatchDates GetAvailableMatchDatesInstance() // Create AvailableMatchDates instance var logger = NullLogger.Instance; var tzConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter( - new NodaTime.TimeZones.DateTimeZoneCache(NodaTime.TimeZones.TzdbDateTimeZoneSource.Default), "Europe/Berlin", - CultureInfo.CurrentCulture, - NodaTime.TimeZones.Resolvers.LenientResolver); + "Europe/Berlin", CultureInfo.CurrentCulture); var availableMatchDates = new AvailableMatchDates(tenantContextMock.Object, tzConverter, logger); return availableMatchDates; } diff --git a/TournamentManager/TournamentManager.Tests/Plan/MatchSchedulerTests.cs b/TournamentManager/TournamentManager.Tests/Plan/MatchSchedulerTests.cs index be3e273..603995c 100644 --- a/TournamentManager/TournamentManager.Tests/Plan/MatchSchedulerTests.cs +++ b/TournamentManager/TournamentManager.Tests/Plan/MatchSchedulerTests.cs @@ -193,9 +193,7 @@ private MatchScheduler GetMatchSchedulerInstance() // Create MatchScheduler instance var logger = NullLogger.Instance; var tzConverter = new Axuno.Tools.DateAndTime.TimeZoneConverter( - new NodaTime.TimeZones.DateTimeZoneCache(NodaTime.TimeZones.TzdbDateTimeZoneSource.Default), "Europe/Berlin", - CultureInfo.CurrentCulture, - NodaTime.TimeZones.Resolvers.LenientResolver); + "Europe/Berlin", CultureInfo.CurrentCulture); _tenantContext = tenantContextMock.Object; _tenantContext.TournamentContext.MatchPlanTournamentId = 1;