diff --git a/source/trx2junit.Core/Extensions/TimeExtensions.cs b/source/trx2junit.Core/Extensions/TimeExtensions.cs index cbd767b..7ddd290 100644 --- a/source/trx2junit.Core/Extensions/TimeExtensions.cs +++ b/source/trx2junit.Core/Extensions/TimeExtensions.cs @@ -23,23 +23,24 @@ public static string ToJUnitDateTime(this DateTime dt) return string.Create(19, dt, static (buffer, value) => FormatDateTime(buffer, value)); } //------------------------------------------------------------------------- - public static string ToTrxDateTime(this DateTime dt) + public static string ToTrxDateTime(this DateTimeOffset dt) { - dt = dt.ToUniversalTime(); - return string.Create(19 + 1 + 3 + 6, dt, static (buffer, value) => { s_trxDateTimeTemplate.CopyTo(buffer); - FormatDateTime(buffer, value); + value.Millisecond.TryFormat(buffer.Slice(20), out int written, "000"); Debug.Assert(written == 3); + + value.Offset.Hours.TryFormat(buffer.Slice(24), out written, "00"); + Debug.Assert(written == 2); }); } //------------------------------------------------------------------------- public static string ToJUnitTime(this double value) => value.ToString("0.000", CultureInfo.InvariantCulture); //------------------------------------------------------------------------- - public static DateTime? ParseDateTime(this string value) + public static DateTimeOffset? ParseDateTime(this string value) { ReadOnlySpan span = value.AsSpan(); @@ -70,34 +71,37 @@ public static string ToTrxDateTime(this DateTime dt) return null; } - int millisecond = 0; - DateTimeKind dateTimeKind = DateTimeKind.Local; + int millisecond = 0; + TimeSpan offset = TimeSpan.Zero; if (value.Length == 29) { if (!span[20..24].TryParse3DigitIntFast(out millisecond)) return null; - dateTimeKind = DateTimeKind.Utc; + if (!span[24..25].TryParse2DigitIntFast(out int offsetHours)) + return null; + + offset = TimeSpan.FromHours(offsetHours); } - return new DateTime(year, month, day, hour, minute, second, millisecond, dateTimeKind); + return new DateTimeOffset(year, month, day, hour, minute, second, millisecond, offset); } catch { return SlowPath(value); } //--------------------------------------------------------------------- - static DateTime? SlowPath(string value) + static DateTimeOffset? SlowPath(string value) { - if (!DateTime.TryParse(value, out DateTime dt)) + if (!DateTimeOffset.TryParse(value, out DateTimeOffset dt)) return null; return dt; } } //------------------------------------------------------------------------- - private static void FormatDateTime(Span buffer, DateTime value) + private static void FormatDateTime(Span buffer, DateTimeOffset value) { #if NET6_0_OR_GREATER if (Sse41.IsSupported && Globals.VectorsEnabled) @@ -112,7 +116,7 @@ private static void FormatDateTime(Span buffer, DateTime value) } //------------------------------------------------------------------------- [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void FormatDateTimeScalar(Span buffer, DateTime value) + private static void FormatDateTimeScalar(Span buffer, DateTimeOffset value) { Debug.Assert(s_junitTimeTemplate.Length <= buffer.Length); s_junitTimeTemplate.CopyTo(buffer); @@ -160,7 +164,7 @@ private static bool TryParseDateTimeScalar( //------------------------------------------------------------------------- #if NET6_0_OR_GREATER [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void FormatDateTimeSse41(Span buffer, DateTime value) + private static void FormatDateTimeSse41(Span buffer, DateTimeOffset value) { Debug.Assert(buffer.Length >= 19); diff --git a/source/trx2junit.Core/Extensions/XElementExtensions.cs b/source/trx2junit.Core/Extensions/XElementExtensions.cs index 9513123..3fdc4f5 100644 --- a/source/trx2junit.Core/Extensions/XElementExtensions.cs +++ b/source/trx2junit.Core/Extensions/XElementExtensions.cs @@ -8,7 +8,7 @@ namespace gfoidl.Trx2Junit.Core; internal static class XElementExtensions { - public static DateTime? ReadDateTime(this XElement element, string attributeName) + public static DateTimeOffset? ReadDateTime(this XElement element, string attributeName) { string? value = (string?)element.Attribute(attributeName); return value!.ParseDateTime(); @@ -68,7 +68,7 @@ public static bool Write(this XElement element, string attributeName, T? null return true; } //------------------------------------------------------------------------- - public static bool WriteTrxDateTime(this XElement element, string attributeName, DateTime? value) + public static bool WriteTrxDateTime(this XElement element, string attributeName, DateTimeOffset? value) { if (!value.HasValue) return false; diff --git a/source/trx2junit.Core/Internal/junit2trx/JUnitTestResultXmlParser.cs b/source/trx2junit.Core/Internal/junit2trx/JUnitTestResultXmlParser.cs index d4da1c8..fa14c62 100644 --- a/source/trx2junit.Core/Internal/junit2trx/JUnitTestResultXmlParser.cs +++ b/source/trx2junit.Core/Internal/junit2trx/JUnitTestResultXmlParser.cs @@ -52,7 +52,7 @@ private static JUnitTestSuite ParseTestSuite(XElement xTestSuite) FailureCount = xTestSuite.ReadInt("failures"), SkippedCount = xTestSuite.ReadInt("skipped"), TimeInSeconds = xTestSuite.ReadDouble("time"), - TimeStamp = xTestSuite.ReadDateTime("timestamp")!.Value, + TimeStamp = xTestSuite.ReadDateTime("timestamp")!.Value.UtcDateTime }; int? testCount = xTestSuite.ReadInt("tests"); diff --git a/source/trx2junit.Core/Internal/trx2junit/Trx2JunitTestConverter.cs b/source/trx2junit.Core/Internal/trx2junit/Trx2JunitTestConverter.cs index 6467207..5000cde 100644 --- a/source/trx2junit.Core/Internal/trx2junit/Trx2JunitTestConverter.cs +++ b/source/trx2junit.Core/Internal/trx2junit/Trx2JunitTestConverter.cs @@ -61,7 +61,7 @@ private void AddTestSuite(string testSuiteName, IEnumerable t if (_counters.TimeStamp.HasValue) { - testSuite.TimeStamp = _counters.TimeStamp.Value; + testSuite.TimeStamp = _counters.TimeStamp.Value.UtcDateTime; } } //------------------------------------------------------------------------- @@ -118,13 +118,13 @@ private void AddTest(JUnitTestSuite junitTestSuite, TrxTestDefinition trxTestDef //------------------------------------------------------------------------- private struct Counters { - public int TestCount; - public int Failures; + public int TestCount; + public int Failures; #pragma warning disable CS0649 - public int Errors; + public int Errors; #pragma warning restore CS0649 - public int Skipped; - public TimeSpan Time; - public DateTime? TimeStamp; + public int Skipped; + public TimeSpan Time; + public DateTimeOffset? TimeStamp; } } diff --git a/source/trx2junit.Core/Models/Trx/TrxTimes.cs b/source/trx2junit.Core/Models/Trx/TrxTimes.cs index 3c42eb4..ff4bd44 100644 --- a/source/trx2junit.Core/Models/Trx/TrxTimes.cs +++ b/source/trx2junit.Core/Models/Trx/TrxTimes.cs @@ -6,10 +6,10 @@ namespace gfoidl.Trx2Junit.Core.Models.Trx; internal sealed class TrxTimes { - public DateTime? Creation { get; set; } - public DateTime? Queuing { get; set; } - public DateTime? Start { get; set; } - public DateTime? Finish { get; set; } + public DateTimeOffset? Creation { get; set; } + public DateTimeOffset? Queuing { get; set; } + public DateTimeOffset? Start { get; set; } + public DateTimeOffset? Finish { get; set; } //------------------------------------------------------------------------- public TimeSpan? RunTime => this.Finish - this.Creation; } diff --git a/source/trx2junit.Core/Models/Trx/TrxUnitTestResult.cs b/source/trx2junit.Core/Models/Trx/TrxUnitTestResult.cs index bded905..e513d5b 100644 --- a/source/trx2junit.Core/Models/Trx/TrxUnitTestResult.cs +++ b/source/trx2junit.Core/Models/Trx/TrxUnitTestResult.cs @@ -6,16 +6,16 @@ namespace gfoidl.Trx2Junit.Core.Models.Trx; internal sealed class TrxUnitTestResult { - public Guid ExecutionId { get; set; } - public Guid TestId { get; set; } - public string? TestName { get; set; } - public TimeSpan? Duration { get; set; } - public DateTime? StartTime { get; set; } - public DateTime? EndTime { get; set; } - public TrxOutcome? Outcome { get; set; } - public string? ComputerName { get; set; } - public string? Message { get; set; } - public string? StackTrace { get; set; } - public string? StdOut { get; set; } - public string? StdErr { get; set; } + public Guid ExecutionId { get; set; } + public Guid TestId { get; set; } + public string? TestName { get; set; } + public TimeSpan? Duration { get; set; } + public DateTimeOffset? StartTime { get; set; } + public DateTimeOffset? EndTime { get; set; } + public TrxOutcome? Outcome { get; set; } + public string? ComputerName { get; set; } + public string? Message { get; set; } + public string? StackTrace { get; set; } + public string? StdOut { get; set; } + public string? StdErr { get; set; } } diff --git a/tests/trx2junit.Core.Tests/Extensions/TimeExtensionsTests/ParseDateTime.cs b/tests/trx2junit.Core.Tests/Extensions/TimeExtensionsTests/ParseDateTime.cs index 18a484b..0300f31 100644 --- a/tests/trx2junit.Core.Tests/Extensions/TimeExtensionsTests/ParseDateTime.cs +++ b/tests/trx2junit.Core.Tests/Extensions/TimeExtensionsTests/ParseDateTime.cs @@ -11,10 +11,10 @@ public class ParseDateTime [Test] public void Valid_JUnitDateTime___OK() { - DateTime now = new DateTime(2019, 11, 10, 15, 33, 27); - string value = now.ToJUnitDateTime(); + DateTimeOffset now = new(2019, 11, 10, 15, 33, 27, TimeSpan.FromHours(1d)); + string value = now.UtcDateTime.ToJUnitDateTime(); - DateTime? actual = value.ParseDateTime(); + DateTimeOffset? actual = value.ParseDateTime(); Assert.Multiple(() => { @@ -36,10 +36,10 @@ public void Brute_force_validation() { for (int second = 0; second < 60; ++second) { - DateTime dt = new DateTime(year, month, day, hour, minute, second); - string value = dt.ToJUnitDateTime(); + DateTimeOffset dt = new(year, month, day, hour, minute, second, TimeSpan.FromHours(1d)); + string value = dt.UtcDateTime.ToJUnitDateTime(); - DateTime? actual = value.ParseDateTime(); + DateTimeOffset? actual = value.ParseDateTime(); Assert.Multiple(() => { @@ -54,16 +54,15 @@ public void Brute_force_validation() [Test] public void Valid_TrxDateTime___OK() { - DateTime now = new(2019, 11, 10, 15, 33, 27, 123); - string value = now.ToTrxDateTime(); + DateTimeOffset now = new(2019, 11, 10, 15, 33, 27, 123, TimeSpan.FromHours(1d)); + string value = now.ToTrxDateTime(); - DateTime? actual = value.ParseDateTime(); + DateTimeOffset? actual = value.ParseDateTime(); Assert.Multiple(() => { Assert.IsTrue(actual.HasValue); - Assert.AreEqual(now.ToUniversalTime(), actual.Value); - Assert.AreEqual(DateTimeKind.Utc, actual.Value.Kind); + Assert.AreEqual(now, actual.Value); }); } //------------------------------------------------------------------------- @@ -75,7 +74,7 @@ public void Valid_TrxDateTime___OK() [TestCase("201a-11-17T12:26:07")] public void Invalid_DateTime_string___null(string value) { - DateTime? actual = value.ParseDateTime(); + DateTimeOffset? actual = value.ParseDateTime(); Assert.IsFalse(actual.HasValue, "actual = {0}", actual); } diff --git a/tests/trx2junit.Core.Tests/Extensions/TimeExtensionsTests/ToTrxDateTime.cs b/tests/trx2junit.Core.Tests/Extensions/TimeExtensionsTests/ToTrxDateTime.cs index d32d1de..e9f847c 100644 --- a/tests/trx2junit.Core.Tests/Extensions/TimeExtensionsTests/ToTrxDateTime.cs +++ b/tests/trx2junit.Core.Tests/Extensions/TimeExtensionsTests/ToTrxDateTime.cs @@ -10,13 +10,15 @@ namespace gfoidl.Trx2Junit.Core.Tests.Extensions.TimeExtensionsTests; public class ToTrxDateTime { [Test, TestCaseSource(nameof(DateTime_given___correct_format_TestCases))] - public string DateTime_given___correct_format(DateTime dateTime) + public string DateTime_given___correct_format(DateTimeOffset dateTime) { return dateTime.ToTrxDateTime(); } //------------------------------------------------------------------------- private static IEnumerable DateTime_given___correct_format_TestCases() { - yield return new TestCaseData(new DateTime(2019, 11, 10, 15, 33, 27, 446, DateTimeKind.Utc)).Returns("2019-11-10T15:33:27.446+00:00"); + yield return new TestCaseData(new DateTimeOffset(2019, 11, 10, 15, 33, 27, 446, TimeSpan.FromHours(0d))).Returns("2019-11-10T15:33:27.446+00:00"); + yield return new TestCaseData(new DateTimeOffset(2019, 11, 10, 15, 33, 27, 446, TimeSpan.FromHours(1d))).Returns("2019-11-10T15:33:27.446+01:00"); + yield return new TestCaseData(new DateTimeOffset(2019, 11, 10, 15, 33, 27, 446, TimeSpan.FromHours(2d))).Returns("2019-11-10T15:33:27.446+02:00"); } } diff --git a/tests/trx2junit.Core.Tests/Extensions/XElementExtensionsTests/ReadDateTime.cs b/tests/trx2junit.Core.Tests/Extensions/XElementExtensionsTests/ReadDateTime.cs index ba66207..f04f049 100644 --- a/tests/trx2junit.Core.Tests/Extensions/XElementExtensionsTests/ReadDateTime.cs +++ b/tests/trx2junit.Core.Tests/Extensions/XElementExtensionsTests/ReadDateTime.cs @@ -12,10 +12,10 @@ public class ReadDateTime [Test] public void Xml_with_valid_DateTime_given___OK() { - DateTime now = new DateTime(2019, 11, 10, 15, 33, 27); - var xml = new XElement("data", new XAttribute("dt", now.ToJUnitDateTime())); + DateTimeOffset now = new(2019, 11, 10, 15, 33, 27, TimeSpan.FromHours(1d)); + var xml = new XElement("data", new XAttribute("dt", now.UtcDateTime.ToJUnitDateTime())); - DateTime? actual = xml.ReadDateTime("dt"); + DateTimeOffset? actual = xml.ReadDateTime("dt"); Assert.Multiple(() => { @@ -29,7 +29,7 @@ public void Xml_without_DateTime_value___null() { var xml = new XElement("data", new XAttribute("foo", "123")); - DateTime? actual = xml.ReadDateTime("foo"); + DateTimeOffset? actual = xml.ReadDateTime("foo"); Assert.IsFalse(actual.HasValue); } @@ -39,7 +39,7 @@ public void Attribute_does_not_exists_on_xml___null() { var xml = new XElement("data"); - DateTime? actual = xml.ReadDateTime("foo"); + DateTimeOffset? actual = xml.ReadDateTime("foo"); Assert.IsFalse(actual.HasValue); } diff --git a/tests/trx2junit.Core.Tests/Extensions/XElementExtensionsTests/WriteTrxDateTime.cs b/tests/trx2junit.Core.Tests/Extensions/XElementExtensionsTests/WriteTrxDateTime.cs index 34d8870..4ca01a4 100644 --- a/tests/trx2junit.Core.Tests/Extensions/XElementExtensionsTests/WriteTrxDateTime.cs +++ b/tests/trx2junit.Core.Tests/Extensions/XElementExtensionsTests/WriteTrxDateTime.cs @@ -24,9 +24,9 @@ public void Null_given___no_attribute_added() [Test] public void Non_null_given___attribute_added() { - DateTime? value = DateTime.Now; - var xmlExpected = new XElement("root", new XAttribute("dt", value.Value.ToTrxDateTime())); - var xml = new XElement("root"); + DateTimeOffset? value = DateTimeOffset.Now; + var xmlExpected = new XElement("root", new XAttribute("dt", value.Value.ToTrxDateTime())); + var xml = new XElement("root"); xml.WriteTrxDateTime("dt", value); diff --git a/tests/trx2junit.Core.Tests/Internal/JUnit2TrxTestConverterTests/SetCreationTimestamp.cs b/tests/trx2junit.Core.Tests/Internal/JUnit2TrxTestConverterTests/SetCreationTimestamp.cs index c7e9045..b4ae2be 100644 --- a/tests/trx2junit.Core.Tests/Internal/JUnit2TrxTestConverterTests/SetCreationTimestamp.cs +++ b/tests/trx2junit.Core.Tests/Internal/JUnit2TrxTestConverterTests/SetCreationTimestamp.cs @@ -15,30 +15,30 @@ public class SetCreationTimestamp public void TrxTimes_Creation_is_null___TimeStamp_set() { var trxTimes = new TrxTimes(); - var junitTestSuite = new JUnitTestSuite { TimeStamp = DateTime.Now }; + var junitTestSuite = new JUnitTestSuite { TimeStamp = DateTimeOffset.Now.UtcDateTime }; JUnit2TrxTestConverter.SetCreationTimestamp(junitTestSuite, trxTimes); - Assert.AreEqual(junitTestSuite.TimeStamp, trxTimes.Creation); + Assert.AreEqual(junitTestSuite.TimeStamp, trxTimes.Creation.Value.UtcDateTime); } //------------------------------------------------------------------------- [Test] public void TrxTimes_Creation_has_value_TimeStamp_is_smaller___TimeStamp_set() { - var trxTimes = new TrxTimes { Creation = DateTime.Now }; - var junitTestSuite = new JUnitTestSuite { TimeStamp = DateTime.Now.AddSeconds(-1) }; + var trxTimes = new TrxTimes { Creation = DateTimeOffset.Now }; + var junitTestSuite = new JUnitTestSuite { TimeStamp = DateTimeOffset.Now.UtcDateTime.AddSeconds(-1) }; JUnit2TrxTestConverter.SetCreationTimestamp(junitTestSuite, trxTimes); - Assert.AreEqual(junitTestSuite.TimeStamp, trxTimes.Creation.Value); + Assert.AreEqual(junitTestSuite.TimeStamp, trxTimes.Creation.Value.UtcDateTime); } //------------------------------------------------------------------------- [Test] public void TrxTimes_Creation_has_value_TimeStamp_is_greater___nop() { - DateTime now = DateTime.Now; + DateTimeOffset now = DateTimeOffset.Now; var trxTimes = new TrxTimes { Creation = now }; - var junitTestSuite = new JUnitTestSuite { TimeStamp = DateTime.Now.AddSeconds(1) }; + var junitTestSuite = new JUnitTestSuite { TimeStamp = DateTimeOffset.Now.UtcDateTime.AddSeconds(1) }; JUnit2TrxTestConverter.SetCreationTimestamp(junitTestSuite, trxTimes);