Skip to content

bucketRange

bucketRange(start: string, end: string, unit: SmallestUnit<ZoneBucketUnit>, timeZone: string): string[]

Return the start instant of every unit bucket the half-open range [start, end) touches, bucketed in timeZone.

  • This is “group by day in America/New_York” over UTC timestamps, done correctly. Buckets are not uniform in length and that is the point: a day that springs forward is 23 hours and one that falls back is 25, so a daily aggregate built on a fixed 24 hours drifts an hour twice a year, and a container’s chargeable days — counted in terminal-local calendar days — come out wrong.
  • Each entry is a boundary floorToZone would return, so bucket i runs from result[i] up to (not including) result[i + 1], and the last runs to the next boundary after it.
  • The range is half-open: an end landing exactly on a boundary does not open that bucket. A zero-length range mid-bucket still returns the one bucket holding it; on a boundary it returns [].
  • Both endpoints are read in timeZone. A bracketed zone they carry never supplies the zone, as in floorToZone.
  • Weeks start on Monday (ISO 8601). unit is the same four-unit set floorToZone takes, singular or plural, which isValidZoneBucketUnit narrows.
  • A local boundary that does not exist is not invented: the day Samoa deleted crossing the date line is absent, a local day whose midnight is skipped starts at 01:00, and in a zone that falls back by half an hour the local 01:00 hour bucket is 90 minutes long.
  • A bucket is returned when its start is representable, even if the bucket after it would start past Temporal’s last instant (+275760-09-13T00:00:00Z).
  • Returns [] on invalid input, when start is after end, when the range would need more than 10,000 buckets, when a boundary in it is not representable (see floorToZone), and if a zone ever presented more transitions inside one bucket than the stepper walks — no IANA zone does, and the whole table was swept to confirm it. A truncated list would be worse than the sentinel, so none is returned in any of those cases.
  • Limit at an offset with seconds. The instant is placed in the offset’s whole-minute zone, moved by its seconds, and the moved instant must be inside Temporal’s range. So within the offset’s seconds (under a minute) of the last instant Temporal supports (+275760-09-13T00:00:00Z) for an offset east of UTC, or of the first (-271821-04-20T00:00:00Z) for one west, this returns []. An offset to the minute has no such limit.
ParameterTypeDescription
startstringISO 8601 instant string for the range start (inclusive)
endstringISO 8601 instant string for the range end (exclusive)
unitSmallestUnit<ZoneBucketUnit>bucket unit (“hour” | “day” | “week” | “month”, or its plural)
timeZonestringIANA name or UTC offset the buckets are computed in: a time zone identifier (+05:30, +0530, -08) or a stored offset (±HH:MM[:SS], what getTimeZoneOffset returns)

array of UTC instant strings ending in “Z”, or [] on invalid input

import { bucketRange } from "@northguild/gmt/calendar/calculate";
bucketRange("2024-06-15T03:00:00Z", "2024-06-17T03:00:00Z", "day", "America/New_York") // ["2024-06-14T04:00:00Z", "2024-06-15T04:00:00Z", "2024-06-16T04:00:00Z"]
output 

calendar/calculate/bucketRange.ts