Guides / Going further
Coming from Moment
Move off Moment and moment-timezone to immutable values, zero bundled timezone data, and incremental Date interop.
Moment and moment-timezone carried JavaScript date handling for years, at the cost of mutable objects, zero-indexed months, and heavy timezone bundles.
Migrating removes defensive clones and bundle bloat while letting you update large repositories one module at a time.
Every value is immutable
Adding days or shifting to the start of a month in Moment mutates that instance in place. Passing a shared date into a helper can silently corrupt the caller’s state unless everyone remembers to clone first.
Every value here is immutable. Methods like plus() and in() always return a fresh instance and leave the original untouched.
import { date } from 'native-dates';
const start = date('2026-11-16');
const deadline = start.plus(7, 'days');
start.toString(); // '2026-11-16'
deadline.toString(); // '2026-11-23'Drop defensive clone calls
You can delete every .clone() call during migration. Immutable addition and subtraction aliases are included too, so existing arithmetic callsites keep working.
Dedicated types and 1-indexed months
Moment wraps every concept in one class and numbers months from 0 to 11, where month 10 means November. Here, month is a plain property running from 1 through 12.
Pick date() for calendar days without a clock, datetime() for moments in a time zone, and time() for daily clock times.
import { date, datetime } from 'native-dates';
const invoiceDate = date(2026, 11, 16);
invoiceDate.month; // 11, November
const sentAt = datetime('2026-11-16T17:30:00Z', 'America/New_York');
sentAt.hour; // 12Zero bundled timezone data
moment-timezone ships its own copy of the IANA database to the browser.
Core reads time zone rules directly from the runtime’s Temporal and Intl engines. Every IANA zone works out of the box with zero timezone data in your bundle.
import { datetime } from 'native-dates';
const kickoff = datetime('2026-11-16T09:30', 'America/Los_Angeles');
kickoff.in('Europe/London').format('short', 'en-GB'); // '16/11/2026, 17:30'
kickoff.in('Asia/Tokyo').format('short', 'en-US'); // '11/17/26, 2:30 AM'Side-by-side reference
| Moment | Native Dates | Notes |
|---|---|---|
moment() |
now() or today() |
Explicit moment vs calendar day |
moment.tz(str, zone) |
datetime(str, zone) |
Built into core with zero bundled tzdata |
m.clone().add(7, 'days') |
d.plus(7, 'days') |
Always immutable, and add() alias works too |
m.tz('Europe/Berlin') |
dt.in('Europe/Berlin') |
Views the same moment in another zone |
m.month(), m.date() |
d.month, d.day |
Plain properties with months from 1 to 12 |
b.diff(a, 'hours', true) |
a.until(b, 'hours') |
Fractional total in the requested unit |
moment.duration(2, 'h') |
hours(2) or duration('2h') |
Built into core |
m.format('LLL') |
dt.format('long') |
Locale-aware Intl presets with zero locale files |
m.format('YYYY-MM-DD HH:mm') |
dt.format('YYYY-MM-DD HH:mm') |
Load @native-dates/plugins/tokens for custom patterns |
m.fromNow() |
dt.formatRelative() |
Load @native-dates/plugins/relative for relative strings |
Migrating incrementally
Large codebases rarely convert in a single pull request. Call toJSDate() at boundaries that still expect a native JavaScript date, and pass dates straight back into datetime() on the way in.
import { datetime } from 'native-dates';
const sentAt = datetime('2026-11-16T17:30:00Z', 'America/New_York');
const legacy = sentAt.toJSDate();
const restored = datetime(legacy, 'America/New_York');When downstream helpers still call legacy getters like getFullYear() directly on values, import the legacy-date plugin.
Read methods answer in the value’s own time zone while keeping zero-indexed months for drop-in compatibility. Mutating setters throw immediately so hidden in-place edits surface in tests.
import '@native-dates/plugins/legacy-date';
import { datetime } from 'native-dates';
const dt = datetime('2026-11-16T09:30', 'America/Los_Angeles');
dt.getFullYear(); // 2026
dt.getMonth(); // 10, matching Date.prototype.getMonth()
dt.getHours(); // 9, in America/Los_Angeles