2026-04-22 · 4 min
Temporal API JavaScript 2026: Akhirnya Boleh Lupa Moment.js
Temporal API JavaScript akhirnya Stage 4 di TC39 (April 2026). Bun 1.3, Node 23, dan Deno 2.5 sudah support native. Chrome 128+ dan Safari 18+ juga. Untuk SaaS Indonesia yang user-nya mostly mobile Chrome modern: bisa pakai langsung tanpa polyfill.
Saya migrasi 3 project klien dalam 2 minggu terakhir. Berikut catatannya.
Yang Moment.js / date-fns kasih, yang masih sering dipake
// Moment
moment().format('DD MMMM YYYY')
moment().add(7, 'days')
moment('2026-01-01').isBefore(moment('2026-12-31'))
moment().diff(moment().subtract(1, 'year'), 'days')
// date-fns
format(new Date(), 'dd MMMM yyyy', { locale: id })
addDays(new Date(), 7)
isBefore(parse('2026-01-01'), parse('2026-12-31'))
differenceInDays(new Date(), subYears(new Date(), 1))
Yang Temporal kasih, native
const now = Temporal.Now.plainDateISO();
const formatted = now.toLocaleString('id-ID', {
dateStyle: 'long'
}); // "4 Juni 2026"
const nextWeek = now.add({ days: 7 });
const isBefore = Temporal.PlainDate.compare('2026-01-01', '2026-12-31') < 0;
const daysDiff = now.since(now.subtract({ years: 1 })).total('days');
Lebih panjang? Kadang ya, kadang tidak. Tapi lebih SAFE.
Safer karena?
- Timezone explicit. Tidak ada “implicit local time” hell.
// Old: ambiguous
new Date('2026-01-01') // Browser interpret as local? UTC?
// Temporal: explicit
Temporal.PlainDateTime.from('2026-01-01T00:00:00') // No timezone
Temporal.ZonedDateTime.from('2026-01-01T00:00:00+07:00') // Explicit
Temporal.ZonedDateTime.from('2026-01-01T00:00:00[Asia/Jakarta]') // Explicit
Untuk Indonesia SaaS, banyak bug saya dulu karena confusion antara WIB/WIT/UTC. Temporal mengeliminasi ini.
-
Immutable. Setiap operation return new instance. Tidak ada
date.setMonth(...)mutation yang accidentally break upstream code. -
Arithmetic clearer.
// Old: date math gotcha
const lastDay = new Date(2026, 0, 31); // Jan 31
lastDay.setMonth(lastDay.getMonth() + 1); // What date? Feb 31 → Mar 3!
// Temporal: explicit overflow
const lastDay = Temporal.PlainDate.from('2026-01-31');
lastDay.add({ months: 1 }); // 2026-02-28 (clamped to month end)
lastDay.add({ months: 1, overflow: 'reject' }); // Throws RangeError
Saya pernah ngabisin 4 jam debug Feb 31 issue lima tahun lalu. Temporal eliminate this entirely.
Migration strategy
Saya tidak rewrite seluruh codebase. Strategy:
- New code: pakai Temporal.
- Old code: leave alone kecuali ada bug yang related ke date logic.
- Shared helpers: rewrite ke Temporal, deprecate moment-based one.
// helpers/date.ts (new)
import { Temporal } from 'temporal-polyfill'; // Polyfill untuk client-side browsers older
export function formatIndonesian(date: Temporal.PlainDate): string {
return date.toLocaleString('id-ID', { dateStyle: 'long' });
}
export function daysUntil(target: Temporal.PlainDate): number {
const today = Temporal.Now.plainDateISO();
return Math.ceil(target.since(today).total('days'));
}
// helpers/date-legacy.ts (deprecated, kept untuk old code)
import dayjs from 'dayjs';
// ... existing helpers
Build size impact
Bundle saya:
| Library | Size minified+gzip |
|---|---|
| Moment.js | 73 KB |
| date-fns (yang saya pakai) | 18 KB |
| dayjs | 7 KB |
| Temporal (native, no library) | 0 KB |
| temporal-polyfill (untuk older browser) | 15 KB |
Untuk modern-only audience: 0 KB. Untuk include polyfill: 15 KB (less than date-fns).
Yang Temporal belum cover
-
Lunar calendar (Hijriah): untuk app yang display tanggal Hijriah untuk audience Muslim. Saya masih pakai dayjs dengan plugin Hijri. Temporal Calendar API support custom calendars, tapi setup-nya verbose.
-
Business day calculations: hitung “5 hari kerja”, skip weekend dan holiday. Tidak native di Temporal. Saya tulis helper sendiri:
const HOLIDAYS_2026 = new Set([
'2026-01-01', '2026-02-17', /* ... */
]);
function addBusinessDays(start: Temporal.PlainDate, n: number): Temporal.PlainDate {
let current = start;
let remaining = n;
while (remaining > 0) {
current = current.add({ days: 1 });
const dow = current.dayOfWeek;
if (dow >= 1 && dow <= 5 && !HOLIDAYS_2026.has(current.toString())) {
remaining--;
}
}
return current;
}
Saran
Untuk project baru: pakai Temporal. Untuk project existing: migrasi gradual, tidak harus all-at-once.
Untuk junior dev di tim: invest 2 jam baca Temporal docs. Banyak bug date-related yang dulunya “normal” untuk dialami akan hilang.
Stage 4 berarti final. API tidak akan berubah lagi. Aman untuk commit ke ini.
Ditulis oleh Reza Pradipta