Class DbnTime
- Namespace
- DatabentoDotNet.Dbn
- Assembly
- DatabentoDotNet.Dbn.dll
Converts between DBN's on-the-wire ulong nanosecond timestamps and NodaTime's Instant and LocalDate.
public static class DbnTime
- Inheritance
-
DbnTime
- Inherited Members
Examples
using DatabentoDotNet.Dbn;
using NodaTime;
// ts_recv for 2020-12-28T13:00:00.000000001Z. The trailing 1 is a nanosecond, which is what a
// DateTime tick — 100 of them — cannot represent and an Instant can.
ulong raw = 1609160400000000001;
Instant when = DbnTime.ToInstant(raw); // 2020-12-28T13:00:00.000000001Z
LocalDate day = DbnTime.ToUtcDate(raw); // 2020-12-28, the date a symbol map keys on
ulong roundTripped = DbnTime.ToUnixNanoseconds(when); // exactly `raw` again
// The sentinel is not a time, and every conversion here says so rather than answering.
if (!DbnTime.TryToInstant(DbnConstants.UndefTimestamp, out Instant absent))
{
Console.WriteLine("no timestamp");
}
Use the Try pair wherever an absent timestamp is an ordinary outcome — most optional record
fields — and ToInstant(ulong) or ToUtcDate(ulong) where it is not, so a sentinel
throws instead of becoming a plausible wrong answer:
if (record.TryGet(out ImbalanceMsg imbalance)
&& DbnTime.TryToInstant(imbalance.AuctionTime, out Instant auction))
{
Console.WriteLine($"auction at {auction}");
}
Remarks
This is the boundary. Record struct fields and the codec keep raw ulong
nanoseconds, because a record is reinterpreted in place over the read buffer and a field's
type is its wire layout — Instant is 16 bytes and LocalDate
is 4, where the wire has an 8-byte u64. Everything above the codec uses NodaTime.
This type is the only crossing, and it is deliberately explicit: there is no implicit
conversion to fall into.
Every conversion checks UndefTimestamp first, and that check is the whole reason this type exists. The sentinel is MaxValue, and the obvious conversion wraps it silently:
Duration.FromNanoseconds((long)DbnConstants.UndefTimestamp) // -1 ns. No exception.
That resolves to an Instant one nanosecond before the UNIX epoch — 1969-12-31T23:59:59.999999999Z, a confidently wrong answer that nothing downstream would question. The sentinel does no better as a date: it floor-divides to a perfectly plausible day in 2554. So TryToInstant(ulong, out Instant) and TryToUtcDate(ulong, out LocalDate) report "no timestamp" by returning false, and ToInstant(ulong) and ToUtcDate(ulong) throw rather than answer.
Apart from the sentinel, every ulong converts exactly. The conversion
splits into whole days plus a nanosecond-of-day remainder rather than going through a single
long nanosecond count, so it never touches the year-2262 ceiling that
long.MaxValue nanoseconds imposes. MaxValue minus one nanosecond
is in 2554, well inside Instant's range.
Methods
IsUndefined(ulong)
Reports whether a raw timestamp is DBN's "no timestamp" sentinel, UndefTimestamp.
public static bool IsUndefined(ulong unixNanoseconds)
Parameters
unixNanosecondsulongThe raw timestamp to test.
Returns
ToInstant(ulong)
Converts a raw DBN timestamp to an Instant.
public static Instant ToInstant(ulong unixNanoseconds)
Parameters
unixNanosecondsulongNanoseconds since the UNIX epoch, as read from the wire.
Returns
- Instant
The converted instant.
Exceptions
- ArgumentOutOfRangeException
unixNanosecondsis UndefTimestamp, which is not a time. Use TryToInstant(ulong, out Instant) where an absent timestamp is expected.
ToUnixNanoseconds(Instant)
public static ulong ToUnixNanoseconds(Instant instant)
Parameters
instantInstantThe instant to convert.
Returns
- ulong
Nanoseconds since the UNIX epoch.
Exceptions
- ArgumentOutOfRangeException
instantis before the UNIX epoch, or so far after it that it does not fit in a ulong — or that it would land exactly on UndefTimestamp and read back as "no timestamp".
ToUnixNanosecondsAtMidnightUtc(LocalDate)
Converts a UTC calendar date to the raw ulong nanoseconds of its 00:00 UTC midnight.
public static ulong ToUnixNanosecondsAtMidnightUtc(LocalDate date)
Parameters
dateLocalDateThe UTC date.
Returns
- ulong
Nanoseconds since the UNIX epoch at 00:00 UTC on
date.
Remarks
The inverse of ToUtcDate(ulong) only in the direction that loses nothing: every timestamp on a given day maps to that day, and the day maps back to its first nanosecond.
Exceptions
- ArgumentOutOfRangeException
dateis before 1970-01-01, or so far after it that its midnight does not fit in a ulong nanosecond count.
ToUtcDate(ulong)
Converts a raw DBN timestamp to the UTC calendar date it falls on.
public static LocalDate ToUtcDate(ulong unixNanoseconds)
Parameters
unixNanosecondsulongNanoseconds since the UNIX epoch, as read from the wire.
Returns
- LocalDate
The UTC date.
Exceptions
- ArgumentOutOfRangeException
unixNanosecondsis UndefTimestamp. It would otherwise floor-divide to an entirely plausible day in 2554.
TryToInstant(ulong, out Instant)
Converts a raw DBN timestamp to an Instant, unless it is the undefined sentinel.
public static bool TryToInstant(ulong unixNanoseconds, out Instant instant)
Parameters
unixNanosecondsulongNanoseconds since the UNIX epoch, as read from the wire.
instantInstantReceives the converted instant, or default when
unixNanosecondsis UndefTimestamp.
Returns
- bool
false when the timestamp is undefined — the only value that does not convert. Every other ulong converts exactly.
TryToUtcDate(ulong, out LocalDate)
Converts a raw DBN timestamp to the UTC calendar date it falls on, unless it is the undefined sentinel.
public static bool TryToUtcDate(ulong unixNanoseconds, out LocalDate date)
Parameters
unixNanosecondsulongNanoseconds since the UNIX epoch, as read from the wire.
dateLocalDateReceives the UTC date, or default when
unixNanosecondsis UndefTimestamp.
Returns
Remarks
This is the date the symbol maps key on — see TryGetSymbol(LocalDate, uint, out string?).