Struct DateRange
- Namespace
- DatabentoDotNet.Historical
- Assembly
- DatabentoDotNet.Historical.dll
A half-open UTC date interval: an inclusive Start date and an exclusive End date. Every Databento historical endpoint that queries by calendar date, rather than by exact instant, takes one of these.
public readonly record struct DateRange : IEquatable<DateRange>
- Implements
- Inherited Members
Examples
var day = new LocalDate(2024, 1, 2);
DateRange.OnDay(day); // 2024-01-02 .. 2024-01-03
DateRange.Between(day, new LocalDate(2024, 2, 1)); // .. 2024-02-01, exclusive
DateRange.Including(day, new LocalDate(2024, 1, 31)); // the same range, named by its last day
DateRange.Spanning(day, Duration.FromDays(7)); // 2024-01-02 .. 2024-01-09
// Widened to the nanosecond instants the timeseries and metadata endpoints take.
DateTimeRange range = DateRange.OnDay(day).ToDateTimeRange();
// Rejected here rather than by an HTTP 422 that bills for the round trip: End must be strictly
// after Start.
DateRange.Between(day, day); // throws ArgumentException
Remarks
Port of upstream's DateRange (databento-rs/src/historical.rs). Rust spells
"which end is exclusive" with the range type itself — Range<Date> for half-open,
RangeInclusive<Date> for inclusive — because a Rust range is a type. C# has no
range literal to overload against, so the upstream From impls become named factories
instead: OnDay(LocalDate), Between(LocalDate, LocalDate), Including(LocalDate, LocalDate), Spanning(LocalDate, Duration).
Each name says, on its own, which end is exclusive — a half-open interval is exactly the kind
of API a caller misreads silently, and nothing here reuses a bare constructor that would hide
that choice.
An empty or inverted range is rejected at construction — End must be
strictly after Start. Upstream sends whatever a caller built and lets the API
answer with an error. This port fails locally instead, the same way this codebase's
Symbols type already rejects a symbol carrying a character the wire format reserves:
the offending pair is still in the caller's hand at the point it is rejected, rather than
round-tripping to the server to learn the same thing from an HTTP error that bills for the
request. See the M3 ROADMAP decision record for the full reasoning.
Properties
End
The exclusive UTC end date.
public LocalDate End { get; }
Property Value
EndIsoDate
This range's End, rendered the way the historical API's end_date
parameter expects it: yyyy-MM-dd.
public string EndIsoDate { get; }
Property Value
Exceptions
- InvalidOperationException
This is a default DateRange value.
Start
The inclusive UTC start date.
public LocalDate Start { get; }
Property Value
StartIsoDate
This range's Start, rendered the way the historical API's
start_date parameter expects it: yyyy-MM-dd.
public string StartIsoDate { get; }
Property Value
Exceptions
- InvalidOperationException
This is a default DateRange value.
Methods
Between(LocalDate, LocalDate)
A half-open range: start is included, end is not.
public static DateRange Between(LocalDate start, LocalDate end)
Parameters
Returns
- DateRange
The range.
Exceptions
- ArgumentException
endis not strictly afterstart.
Including(LocalDate, LocalDate)
A range where both start and lastDay are included.
public static DateRange Including(LocalDate start, LocalDate lastDay)
Parameters
startLocalDateThe inclusive start date.
lastDayLocalDateThe last day the range covers, inclusive.
Returns
Exceptions
- ArgumentException
lastDayis beforestart.
OnDay(LocalDate)
A range covering exactly one UTC calendar day.
public static DateRange OnDay(LocalDate date)
Parameters
dateLocalDateThe day.
Returns
- DateRange
A range from
dateto the following day.
Spanning(LocalDate, Duration)
A range starting at start and spanning duration,
counting only whole days.
public static DateRange Spanning(LocalDate start, Duration duration)
Parameters
startLocalDateThe inclusive start date.
durationDurationHow long the range spans, truncated down to whole days.
Returns
- DateRange
The range.
Remarks
A calendar date carries no time of day, so a duration shorter than 24 hours contributes no
day to the range at all — this matches upstream's own Date + Duration, which adds
duration.whole_days(). Upstream's own test for this construction
(date_range_from_lt_day_duration) passes a one-second duration and asserts the
resulting range is empty (start == end). This port does not carry that case forward:
Validate(LocalDate, LocalDate, string) runs here exactly as it does in every other factory, so a duration
under one day is rejected rather than silently producing a range that would query no data.
Exceptions
- ArgumentException
durationspans fewer than one whole day.
ToDateTimeRange()
Widens this date range to a DateTimeRange, at UTC midnight on each end.
public DateTimeRange ToDateTimeRange()
Returns
- DateTimeRange
A DateTimeRange whose Start and End are UTC midnight on Start and End respectively.
ToString()
A debugging-oriented description, printing Start and End directly rather than through StartIsoDate/EndIsoDate.
public override string ToString()
Returns
- string
The description.
Remarks
A hand-written override, not the compiler-synthesized record ToString: that
synthesized version prints every public property, including StartIsoDate and
EndIsoDate — which would make a supposedly inert ToString() call throw
InvalidOperationException for a default value, defeating the point of
leaving it unguarded. Printing Start/End instead never throws,
for any value including default.