From e9a18bf5d3dd03eea3748ceca2bf3067f45cdc81 Mon Sep 17 00:00:00 2001 From: Gregory Nikolaishvili Date: Mon, 30 Mar 2026 14:25:10 +0400 Subject: [PATCH 1/3] Update TimeOnly ToXmlString to include time zone offset - Change ToXmlString(TimeOnly) to output "HH:mm:sszzz" with local offset - Update unit test to match new format with time zone info - Bump Verify.Xunit package to v31.12.5 --- Directory.Packages.props | 2 +- src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs | 11 ++++++++++- .../ToXmlStringExtTests.cs | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index cf90f96..41cd5cf 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -14,7 +14,7 @@ - + diff --git a/src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs b/src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs index d3024cb..76cefab 100644 --- a/src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs +++ b/src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs @@ -35,8 +35,17 @@ public static class ToXmlStringExt /// /// The TimeOnly value to convert. /// The XML string representation of the TimeOnly value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string ToXmlString(this TimeOnly value) => value.ToString("HH:mm:ss", CultureInfo.InvariantCulture); + public static string ToXmlString(this TimeOnly value) + { + var date = DateOnly.FromDateTime(DateTime.Today); + + var localDateTime = date.ToDateTime(value, DateTimeKind.Local); + var dto = new DateTimeOffset(localDateTime); + + return dto.ToString("HH:mm:sszzz", CultureInfo.InvariantCulture); + } /// /// Converts a value to its XML string representation diff --git a/tests/AltaSoft.DomainPrimitives.XmlDataTypes.Tests/ToXmlStringExtTests.cs b/tests/AltaSoft.DomainPrimitives.XmlDataTypes.Tests/ToXmlStringExtTests.cs index eef5e8d..e76dfbb 100644 --- a/tests/AltaSoft.DomainPrimitives.XmlDataTypes.Tests/ToXmlStringExtTests.cs +++ b/tests/AltaSoft.DomainPrimitives.XmlDataTypes.Tests/ToXmlStringExtTests.cs @@ -24,7 +24,7 @@ public void TimeOnly_ToXmlString_ReturnsExpectedFormat() { var t = new TimeOnly(13, 45, 30); var xml = t.ToXmlString(); - Assert.Equal("13:45:30", xml); + Assert.Matches(@"^13:45:30(?:Z|[+-]\d{2}:\d{2})$", xml); } [Fact] From 00ba66a6e3b0c64c2fa84b0886ccdb30cfaab922 Mon Sep 17 00:00:00 2001 From: Gregory Nikolaishvili Date: Mon, 30 Mar 2026 14:31:07 +0400 Subject: [PATCH 2/3] Update src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs b/src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs index 76cefab..169fc45 100644 --- a/src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs +++ b/src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs @@ -39,12 +39,17 @@ public static class ToXmlStringExt [MethodImpl(MethodImplOptions.AggressiveInlining)] public static string ToXmlString(this TimeOnly value) { - var date = DateOnly.FromDateTime(DateTime.Today); + // We avoid constructing a local DateTime for an arbitrary date to prevent + // possible exceptions on DST transition days (invalid or ambiguous local times). + var timePart = value.ToString("HH:mm:ss", CultureInfo.InvariantCulture); - var localDateTime = date.ToDateTime(value, DateTimeKind.Local); - var dto = new DateTimeOffset(localDateTime); + // Derive the current local offset from UTC in a DST-safe way using the current instant. + var offset = TimeZoneInfo.Local.GetUtcOffset(DateTime.UtcNow); + var offsetSign = offset < TimeSpan.Zero ? "-" : "+"; + var absoluteOffset = offset.Duration(); + var offsetPart = absoluteOffset.ToString(@"hh\:mm", CultureInfo.InvariantCulture); - return dto.ToString("HH:mm:sszzz", CultureInfo.InvariantCulture); + return $"{timePart}{offsetSign}{offsetPart}"; } /// From 79a6b3a2b6b6484c15b63beb480b47517579bd38 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 30 Mar 2026 10:35:13 +0000 Subject: [PATCH 3/3] fix: update TimeOnly XML doc comment and strengthen timezone offset test Agent-Logs-Url: https://github.com/altasoft/DomainPrimitives/sessions/5b0d89b2-92b7-4b7f-8c6c-75dfa3befa56 Co-authored-by: GregoryNikolaishvili <25057879+GregoryNikolaishvili@users.noreply.github.com> --- src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs | 3 ++- .../ToXmlStringExtTests.cs | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs b/src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs index 169fc45..38236bd 100644 --- a/src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs +++ b/src/AltaSoft.DomainPrimitives/ToXmlStringExt.cs @@ -31,7 +31,8 @@ public static class ToXmlStringExt public static string ToXmlString(this DateOnly value) => value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); /// - /// Converts a value to its XML string representation in the format "HH:mm:ss". + /// Converts a value to its XML string representation in the format "HH:mm:sszzz", + /// where the time zone offset reflects the current local offset from UTC. /// /// The TimeOnly value to convert. /// The XML string representation of the TimeOnly value. diff --git a/tests/AltaSoft.DomainPrimitives.XmlDataTypes.Tests/ToXmlStringExtTests.cs b/tests/AltaSoft.DomainPrimitives.XmlDataTypes.Tests/ToXmlStringExtTests.cs index e76dfbb..abe5315 100644 --- a/tests/AltaSoft.DomainPrimitives.XmlDataTypes.Tests/ToXmlStringExtTests.cs +++ b/tests/AltaSoft.DomainPrimitives.XmlDataTypes.Tests/ToXmlStringExtTests.cs @@ -24,7 +24,15 @@ public void TimeOnly_ToXmlString_ReturnsExpectedFormat() { var t = new TimeOnly(13, 45, 30); var xml = t.ToXmlString(); - Assert.Matches(@"^13:45:30(?:Z|[+-]\d{2}:\d{2})$", xml); + + // Validate the overall shape: time followed by a timezone offset + Assert.Matches(@"^13:45:30[+-]\d{2}:\d{2}$", xml); + + // Validate the offset suffix matches the actual current local offset + var offset = TimeZoneInfo.Local.GetUtcOffset(DateTime.UtcNow); + var offsetSign = offset < TimeSpan.Zero ? "-" : "+"; + var expectedOffsetSuffix = $"{offsetSign}{offset.Duration():hh\\:mm}"; + Assert.EndsWith(expectedOffsetSuffix, xml); } [Fact]