Skip to content
Chapter 83Lesson 6

Quiz - Time, dates, and timezones

Quiz progress

0 / 0

A teammate models an invoice’s dueDate — the day the customer agreed to pay by — as a timestamptz set to midnight UTC, “to keep our options open.” Every test passes on the team’s UTC machines. What ships?

A customer in Sydney who picks “due the 15th” stores their local midnight as 2026-05-14T14:00:00Z — the 14th — and reads the invoice back as due a day early. The fix isn’t careful midnight handling; it’s a date column plus Temporal.PlainDate, where the time-of-day has nowhere to live and there’s no zone to offset.

Nothing visible — midnight UTC is a fine canonical anchor for a calendar day, as long as every read formats it back with the same UTC offset it was written with.

A precision bug only: WHERE due_date = '2026-05-15' misses rows on the dropped-microsecond tail, which precision: 3 on the column resolves.

Your instantColumn hands back a Temporal.Instant automatically on every read, and Instant.from(...) parses it on every write. So you return one straight from a route handler: return Response.json({ createdAt: invoice.createdAt }). It breaks. Why — and what’s the rule?

The database boundary converts in both directions for you, but the wire doesn’t — a Temporal.Instant is a class instance that JSON (and the RSC boundary) can’t carry. You encode it explicitly with .toString() at the edge: Temporal in memory, ISO 8601 on the wire.

Response.json needs the raw Postgres text, so you should read the column with mode: 'string' instead of the custom column and pass that through untouched.

It works fine — JSON.stringify calls Instant.prototype.toJSON(), so the explicit .toString() is redundant either way.

You validate the profile timezone select with z.string().refine((tz) => Intl.supportedValuesOf('timeZone').includes(tz)). It rejects a value users legitimately submit, and the same list leaves a hole in your dropdown. Which value, and what’s the robust fix?

'UTC' — Chromium and Node omit the Etc/* zones from supportedValuesOf, so the membership check rejects your column’s own default. Validate by acceptance instead: try new Intl.DateTimeFormat('en-US', { timeZone: tz }) and accept any zone that constructs without throwing.

'America/New_York'supportedValuesOf lists only canonical zone IDs, so DST-observing aliases fail; normalize to the offset form -05:00 before validating.

None — the membership check is exactly right; the real fix is storing an offset like -05:00 so DST math stays stable across the year.

Sort each recurring job to where it belongs. Select every job that should name an IANA timezone rather than run in UTC.

A B2B customer’s “9 AM weekday summary” email, fired on their local clock.

The monthly invoice stamped with a San Francisco company’s issue date.

The nightly retention sweep that deletes rows older than 30 days.

The hourly metric rollup that feeds the analytics dashboard.

A user moves from New York to Tokyo and updates their profile timezone. What should happen to a one-shot “remind me at 5 PM next Friday” job that was scheduled while they were still in New York?

Nothing automatic — it was collapsed to a fixed Temporal.Instant at scheduling time, so past intent is honored and it fires at the New-York-derived moment. Offer an explicit “rebase your 1 reminder?” prompt if anything, never a silent shift.

It re-registers with the new zone, exactly like the user’s recurring daily summary — any pending work that depended on the old zone tracks the profile change.

It throws on the next read, because the stored instant now disagrees with the profile zone and must be recomputed before it can fire.

anchor is the PlainDate for January 31. Predict the difference:

const a = anchor.add({ months: 1 }).add({ months: 1 });
const b = anchor.add({ months: 2 });

a is March 28 and b is March 31. Adding a month twice clamps at each hop (Jan 31 → Feb 28 → Mar 28), losing the “31st” after the first clamp; one larger call clamps only once, and March has a 31st.

Both are March 31 — add is associative, so iterating one month twice and adding two months in one call always land on the same day.

a throws a RangeError on the Feb 31 step; b is March 31. You need overflow: 'reject' on a to avoid the throw.

Quiz complete

Score by topic