Skip to content

Commit

Permalink
Timestamps honor timezone (#107)
Browse files Browse the repository at this point in the history
* Use DateTimeOffsets

* Updated TimeExtensions
  • Loading branch information
gfoidl authored Jul 8, 2022
1 parent 54183d0 commit 956cf64
Show file tree
Hide file tree
Showing 11 changed files with 74 additions and 69 deletions.
32 changes: 18 additions & 14 deletions source/trx2junit.Core/Extensions/TimeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<char> span = value.AsSpan();

Expand Down Expand Up @@ -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<char> buffer, DateTime value)
private static void FormatDateTime(Span<char> buffer, DateTimeOffset value)
{
#if NET6_0_OR_GREATER
if (Sse41.IsSupported && Globals.VectorsEnabled)
Expand All @@ -112,7 +116,7 @@ private static void FormatDateTime(Span<char> buffer, DateTime value)
}
//-------------------------------------------------------------------------
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void FormatDateTimeScalar(Span<char> buffer, DateTime value)
private static void FormatDateTimeScalar(Span<char> buffer, DateTimeOffset value)
{
Debug.Assert(s_junitTimeTemplate.Length <= buffer.Length);
s_junitTimeTemplate.CopyTo(buffer);
Expand Down Expand Up @@ -160,7 +164,7 @@ private static bool TryParseDateTimeScalar(
//-------------------------------------------------------------------------
#if NET6_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void FormatDateTimeSse41(Span<char> buffer, DateTime value)
private static void FormatDateTimeSse41(Span<char> buffer, DateTimeOffset value)
{
Debug.Assert(buffer.Length >= 19);

Expand Down
4 changes: 2 additions & 2 deletions source/trx2junit.Core/Extensions/XElementExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -68,7 +68,7 @@ public static bool Write<T>(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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ private void AddTestSuite(string testSuiteName, IEnumerable<TrxTestDefinition> t

if (_counters.TimeStamp.HasValue)
{
testSuite.TimeStamp = _counters.TimeStamp.Value;
testSuite.TimeStamp = _counters.TimeStamp.Value.UtcDateTime;
}
}
//-------------------------------------------------------------------------
Expand Down Expand Up @@ -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;
}
}
8 changes: 4 additions & 4 deletions source/trx2junit.Core/Models/Trx/TrxTimes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
24 changes: 12 additions & 12 deletions source/trx2junit.Core/Models/Trx/TrxUnitTestResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(() =>
{
Expand All @@ -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(() =>
{
Expand All @@ -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);
});
}
//-------------------------------------------------------------------------
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<TestCaseData> 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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(() =>
{
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit 956cf64

Please sign in to comment.