Guides / Working with time
Storing and sending
Serialize dates, times, timestamps, and durations cleanly across JSON APIs, SQL columns, URLs, and HTML inputs.
Every type serializes to a standard ISO 8601 string when converted to text or passed to JSON.stringify(). Those strings read straight back into each matching factory on the other side.
JSON and wire formats
Pass values directly into JSON.stringify() without custom replacers or wrapper objects:
import { date, duration, time } from 'native-dates';
const standup = date('2026-11-16').at('9:30am', 'America/Los_Angeles');
JSON.stringify({
startsAt: standup,
dueOn: date('2026-11-16'),
opensAt: time('9:30am'),
length: duration('1h 30m'),
});
// '{"startsAt":"2026-11-16T17:30:00.000Z","dueOn":"2026-11-16","opensAt":"09:30:00","length":"PT1H30M"}'| Type | Wire output | Typical payload field |
|---|---|---|
DateTime |
'2026-11-16T17:30:00.000Z' |
Audit timestamps, flight departures, and meeting instants |
CalendarDate |
'2026-11-16' |
Birthdays, due dates, holidays, and booking nights |
Time |
'09:30:00' |
Store opening hours and daily reminder times |
Duration |
'PT1H30M' |
Timeouts, SLAs, billing spans, and timers |
When an external API, cache key, or integer column expects a number, read epoch on a moment or toMilliseconds() on a duration:
standup.toString(); // '2026-11-16T17:30:00.000Z'
standup.epoch; // 1794850200000
duration('1h 30m').toMilliseconds(); // 5400000Round-tripping back into code
Feed each wire string back into its matching factory when reading JSON responses, database rows, or URL parameters:
import { date, datetime, duration, time } from 'native-dates';
const startsAt = datetime('2026-11-16T17:30:00.000Z', 'America/Los_Angeles');
const dueOn = date('2026-11-16');
const opensAt = time('09:30:00');
const length = duration('PT1H30M');Every DateTime serializes to UTC so two clients recording the same event produce identical strings. Passing a time zone when reading that string back views the exact UTC moment locally without shifting when it happened.
Future and recurring events
For past events like order receipts or server logs, a UTC timestamp alone is complete. Future events scheduled by a person need the IANA zone stored alongside the timestamp.
import { date, datetime } from 'native-dates';
const standup = date('2026-11-16').at('9:30am', 'America/Los_Angeles');
const row = {
startsAt: standup.toString(),
zone: standup.zone, // 'America/Los_Angeles'
};
const nextWeek = datetime(row.startsAt, row.zone).plus(1, 'week');
nextWeek.hour; // 9With the zone preserved, adding weeks or months keeps the local 9:30 AM clock time across daylight saving transitions. It also picks up any future changes lawmakers make to local time rules.
Store IANA names, not offsets
An offset like '-08:00' only describes winter in Los Angeles. Store zone whenever future calendar math must follow local daylight saving shifts.
Database columns
Match your SQL column type to the value you have so the database enforces the same boundaries your application code does.
| Type | SQL column | Why it fits |
|---|---|---|
DateTime |
timestamptz or bigint |
Stores an exact UTC instant with no ambiguity |
CalendarDate |
date |
Stores YYYY-MM-DD without midnight timezone shifts |
Time |
time |
Stores HH:MM:SS without inventing a dummy date |
Duration |
interval or text |
Stores structured intervals or ISO 8601 PT strings |
Avoid bare SQL timestamp columns without time zone for real moments. Database drivers often reinterpret bare timestamps in the server’s local zone.
HTML inputs and URLs
Native <input type="date"> and <input type="time"> controls read and write the exact ISO strings that dates and times produce. URL search parameters carry the same compact format.
import { date, time } from 'native-dates';
// <input type="date" value="2026-11-16">
const due = date('2026-11-16');
due.toString(); // '2026-11-16'
// <input type="time" value="09:30">
const opens = time('09:30');
opens.toString().slice(0, 5); // '09:30'
const params = new URLSearchParams({ from: due.toString() });
params.toString(); // 'from=2026-11-16'Reading date inputs with date() also avoids the classic new Date('2026-11-16') bug, where UTC midnight shifts back a day in Western Hemisphere zones.