Repository navigation
Shift and Expand on ranges (move by a delta, buffer both sides) #101
Description
Intervals.NET offers Shift and Expand through its domain packages (https://www.nuget.org/packages/Intervals.NET); Guava's ContiguousSet and Boost.ICL expose the arithmetic through the domain type. The library has no way to move a range or to grow it on both sides, so "the same slot one week later" and "the booking plus a 15-minute buffer on each side" are a deconstruct-and-rebuild per shape today.
Proposed API (per type, since T arithmetic is not generic):
Shift(delta)—DateRange.Shift(int days),DateTimeRange.Shift(TimeSpan),DateTimeOffsetRange.Shift(TimeSpan),TimeRange.Shift(TimeSpan),Int32Range.Shift(int),Int64Range.Shift(long),DecimalRange.Shift(decimal); NodaTimeLocalDateRange.Shift(Period),InstantRange.Shift(Duration),YearMonthRange.Shift(int months). Bounds and inclusiveness move together;EmptyandInfinitereturn themselves; an unbounded side stays unbounded.Expand(amount)— grows both bounds outward byamount(a negative amount shrinks; a result that inverts isEmpty, asCreateFinitealready does).
Overflow at the extremes (int.MaxValue, DateOnly.MaxValue) throws rather than wrapping, like NextValueAfter returning null.
Translation: PostgreSQL has no range arithmetic, so the rebuilt constructor: int4range(lower(r) + @d, upper(r) + @d) for the discrete types (canonical [), so the flags are fixed), and for the continuous ones numrange(lower(r) + @d, upper(r) + @d, CASE … lower_inc/upper_inc … END) with the same flag handling CreateFinite uses (E08 requires constant flags today; this needs the CASE form or stays in-memory and says so).
Done when: Shift and Expand are in docs/ranges.md, held by claims, swept by the small-model oracle (Shift(d).Shift(-d) == this, Expand(a).Contains(this)), and either translated with a live test or listed under "runs in memory only" with the reason.
Found by the comparable-library survey (Intervals.NET), 2026-10-17.
Activity
CaffeinatedCoder commentedon Oct 18, 2026
Decided in #185:
ShiftandExpandrun in memory only. TheCASE-on-flags rebuild was measured on PostgreSQL 18 and differs from memory four ways:'empty'rebuilds as(,),time + intervalwraps at midnight (TimeRangethrows),timestamptz + interval '1 day'is 23 hours across a DST change in the session time zone, and'infinity'::date + 1staysinfinity(DateOnly.MaxValueoverflows). Listed underdocs/efcore.md#what-runs-wherewith those reasons.LocalDateTimeRangegets the members too.