Get the Start and End of Day in a Timezone

To get the start of a day in a zone, use zonedDateTime.startOfDay(); the end is the next day's start (exclusive). This handles DST days where midnight can shift. Part of Timezone Offset Math Explained.

Why this scenario is tricky

Day boundaries are a favourite source of reporting bugs. "Start of day" is not always 00:00: on a spring-forward day in zones that transition at midnight, 00:00 does not exist and the day begins at 01:00. Setting the time to midnight by hand produces an invalid instant; startOfDay() asks the timezone database for the real first moment.

Midnight is not always 00:00Hand-setting midnight can hit a DST gapMidnight is not always 00:00set hours to 0,0,0invalid on some DST dayszdt.startOfDay()the real first instantLet Temporal find the day's first valid moment rather than assuming midnight exists.

Minimal working solution

import { Temporal } from '@js-temporal/polyfill';
const day = Temporal.Now.zonedDateTimeISO('America/Sao_Paulo');
const start = day.startOfDay();          // first instant of the day
const end = start.add({ days: 1 });      // exclusive end = next day's start

Day boundariesstartOfDay, then next day's startDay boundarieszoned daystartOfDay()+1 day = end

Full production version

import { Temporal } from '@js-temporal/polyfill';
function dayBounds(date: Temporal.PlainDate, zone: string) {
  const start = date.toZonedDateTime(zone).startOfDay();
  // Half-open [start, end): use for BETWEEN queries.
  return { start, end: start.add({ days: 1 }) };
}

Half-open day rangeReturn [start, next-start) for queriesHalf-open day rangedate+zonestartOfDay+1 day[start,end)

Verification snippet

Get the Start and End of Day in a Timezone — assertionsKey cases assert correctlyAssertions that prove the edge casestartOfDay normal00:00spring-forward day01:00day length23/24/25hendnext day's start

Common pitfalls

Start & End of Day pitfallsCommon mistakes and their fixesWrongRightset time to 00:00:00invalid at DST gapzdt.startOfDay()real first instantend = 23:59:59.999misses a secondend = start + 1 dayexact half-open

Frequently Asked Questions

How do I get the start of the day in a specific time zone?

Build a ZonedDateTime in that zone and call startOfDay(). It returns the first valid instant of the calendar day, which is 00:00 on normal days but can be 01:00 on a spring-forward day where midnight does not exist.

How should I represent the end of the day for a query?

Use the next day's startOfDay() as an exclusive upper bound and query the half-open range [start, end). That avoids the classic 23:59:59.999 approach, which can miss events in the final fraction of a second.