Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions DateTimeNano.Tests/DateTimeNanoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,61 @@ public void Now_ShouldReturnRecentUtcTime()
Assert.That(now.ToDateTimeUtc(), Is.LessThanOrEqualTo(after));
}

// ── DateTimeOffset interop ────────────────────────────────────────────────

[Test]
public void Constructor_DateTimeOffset_UtcOffset_ShouldStoreCorrectNanoseconds()
{
var dto = new DateTimeOffset(2025, 2, 10, 20, 27, 12, 123, TimeSpan.Zero);
var nano = new Seerstone.DateTimeNano(dto);
Assert.That(nano.NanosecondsSinceEpoch, Is.EqualTo(1_739_219_232_123_000_000UL));
}

[Test]
public void Constructor_DateTimeOffset_NonUtcOffset_ShouldConvertToUtc()
{
// +05:30 (IST) β€” same instant as 15:00 UTC
var dto = new DateTimeOffset(2025, 2, 10, 20, 30, 0, TimeSpan.FromHours(5.5));
var expected = new Seerstone.DateTimeNano(dto.UtcDateTime);
var actual = new Seerstone.DateTimeNano(dto);
Assert.That(actual.NanosecondsSinceEpoch, Is.EqualTo(expected.NanosecondsSinceEpoch));
}

[Test]
public void ToDateTimeOffsetUtc_ShouldReturnUtcOffset()
{
var nano = new Seerstone.DateTimeNano(1_739_219_232_123_000_000UL);
var dto = nano.ToDateTimeOffsetUtc();
Assert.That(dto.Offset, Is.EqualTo(TimeSpan.Zero));
Assert.That(dto.UtcDateTime, Is.EqualTo(new DateTime(2025, 2, 10, 20, 27, 12, 123, DateTimeKind.Utc)));
}

[Test]
public void ImplicitOperator_DateTimeNanoToDateTimeOffset_ShouldBeUtc()
{
var nano = new Seerstone.DateTimeNano(1_739_219_232_123_000_000UL);
DateTimeOffset dto = nano;
Assert.That(dto.Offset, Is.EqualTo(TimeSpan.Zero));
Assert.That(dto.UtcDateTime, Is.EqualTo(nano.ToDateTimeUtc()));
}

[Test]
public void ImplicitOperator_DateTimeOffsetToDateTimeNano_RoundTrips()
{
var original = new DateTimeOffset(2025, 2, 10, 20, 27, 12, 123, TimeSpan.Zero);
Seerstone.DateTimeNano nano = original;
DateTimeOffset roundTripped = nano;
Assert.That(roundTripped, Is.EqualTo(original));
}

[Test]
public void ImplicitOperator_NonUtcDateTimeOffset_ShouldNormalisedToUtc()
{
var dto = new DateTimeOffset(2025, 2, 10, 21, 27, 12, 123, TimeSpan.FromHours(1));
Seerstone.DateTimeNano nano = dto;
Assert.That(nano.ToDateTimeUtc(), Is.EqualTo(dto.UtcDateTime));
}

// ── Parts constructor ─────────────────────────────────────────────────────

[Test]
Expand Down
23 changes: 23 additions & 0 deletions DateTimeNano/DateTimeNano.cs
Original file line number Diff line number Diff line change
Expand Up @@ -442,5 +442,28 @@ public override string ToString()

/// <summary>Implicitly converts a <see cref="System.DateTime"/> to a <see cref="DateTimeNano"/>.</summary>
public static implicit operator DateTimeNano(DateTime d) => new DateTimeNano(d);

/// <summary>
/// Create a <see cref="DateTimeNano"/> from a <see cref="System.DateTimeOffset"/>.
/// The offset is converted to UTC before the nanosecond value is computed,
/// so the stored timestamp always represents the UTC instant.
/// </summary>
/// <param name="datetimeOffset">The source <see cref="System.DateTimeOffset"/>.</param>
public DateTimeNano(DateTimeOffset datetimeOffset)
: this(datetimeOffset.UtcDateTime) { }

/// <summary>
/// Returns the UTC date and time as a <see cref="System.DateTimeOffset"/> with a zero UTC offset.
/// Sub-microsecond nanoseconds are not representable in <see cref="System.DateTimeOffset"/>
/// and are truncated; access them via <see cref="Nanoseconds"/>.
Comment on lines +456 to +458
/// </summary>
/// <returns>A <see cref="System.DateTimeOffset"/> in UTC.</returns>
public DateTimeOffset ToDateTimeOffsetUtc() => new DateTimeOffset(DateTime, TimeSpan.Zero);

/// <summary>Implicitly converts a <see cref="DateTimeNano"/> to a UTC <see cref="System.DateTimeOffset"/>.</summary>
public static implicit operator DateTimeOffset(DateTimeNano d) => d.ToDateTimeOffsetUtc();

/// <summary>Implicitly converts a <see cref="System.DateTimeOffset"/> to a <see cref="DateTimeNano"/>.</summary>
public static implicit operator DateTimeNano(DateTimeOffset d) => new DateTimeNano(d);
}
}