Skip to content
native-dates

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(); // 5400000

Round-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; // 9

With 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.

Edit this pageUpdated Nov 12, 2026