Back to Blog
JavaScriptTemporalDateTime

JavaScript Temporal API Guide

Master the JavaScript Temporal API for modern date and time handling with proper timezone support.

B
Bootspring Team
Engineering
February 4, 2019
5 min read

The Temporal API provides modern date/time handling with proper timezone support, replacing the problematic Date object. Here's how to use it.

Core Concepts#

1// Temporal has several types for different use cases 2 3// Temporal.Instant - exact moment in time (like a timestamp) 4const instant = Temporal.Instant.from('2024-03-15T10:30:00Z'); 5 6// Temporal.ZonedDateTime - date/time with timezone 7const zoned = Temporal.ZonedDateTime.from('2024-03-15T10:30:00[America/New_York]'); 8 9// Temporal.PlainDateTime - date/time without timezone 10const dateTime = Temporal.PlainDateTime.from('2024-03-15T10:30:00'); 11 12// Temporal.PlainDate - just the date 13const date = Temporal.PlainDate.from('2024-03-15'); 14 15// Temporal.PlainTime - just the time 16const time = Temporal.PlainTime.from('10:30:00'); 17 18// Temporal.Duration - a length of time 19const duration = Temporal.Duration.from({ hours: 2, minutes: 30 });

Creating Dates#

1// Current date/time 2const now = Temporal.Now.instant(); 3const nowZoned = Temporal.Now.zonedDateTimeISO('America/New_York'); 4const today = Temporal.Now.plainDateISO(); 5 6// From string 7const date1 = Temporal.PlainDate.from('2024-03-15'); 8const time1 = Temporal.PlainTime.from('14:30:00'); 9const dateTime1 = Temporal.PlainDateTime.from('2024-03-15T14:30:00'); 10 11// From components 12const date2 = Temporal.PlainDate.from({ 13 year: 2024, 14 month: 3, 15 day: 15, 16}); 17 18const time2 = Temporal.PlainTime.from({ 19 hour: 14, 20 minute: 30, 21 second: 0, 22}); 23 24// From existing date 25const legacyDate = new Date(); 26const instant = Temporal.Instant.fromEpochMilliseconds(legacyDate.getTime());

Timezones#

1// Create zoned date/time 2const meeting = Temporal.ZonedDateTime.from({ 3 timeZone: 'America/New_York', 4 year: 2024, 5 month: 3, 6 day: 15, 7 hour: 10, 8 minute: 0, 9}); 10 11console.log(meeting.toString()); 12// 2024-03-15T10:00:00-04:00[America/New_York] 13 14// Convert timezone 15const meetingInTokyo = meeting.withTimeZone('Asia/Tokyo'); 16console.log(meetingInTokyo.toString()); 17// 2024-03-15T23:00:00+09:00[Asia/Tokyo] 18 19// Get offset 20console.log(meeting.offset); // '-04:00' 21 22// Available timezones 23const timezones = Temporal.TimeZone.from('America/New_York');

Arithmetic#

1const date = Temporal.PlainDate.from('2024-03-15'); 2 3// Add duration 4const nextWeek = date.add({ days: 7 }); 5const nextMonth = date.add({ months: 1 }); 6const nextYear = date.add({ years: 1 }); 7 8// Subtract duration 9const lastWeek = date.subtract({ days: 7 }); 10 11// Multiple units 12const future = date.add({ 13 years: 1, 14 months: 2, 15 days: 15, 16}); 17 18// With time 19const dateTime = Temporal.PlainDateTime.from('2024-03-15T10:30:00'); 20const later = dateTime.add({ hours: 2, minutes: 30 });

Comparing Dates#

1const date1 = Temporal.PlainDate.from('2024-03-15'); 2const date2 = Temporal.PlainDate.from('2024-03-20'); 3 4// Comparison methods 5console.log(Temporal.PlainDate.compare(date1, date2)); // -1 (before) 6console.log(date1.equals(date2)); // false 7 8// Calculate difference 9const diff = date1.until(date2); 10console.log(diff.days); // 5 11 12// Different units 13const diff2 = date1.until(date2, { largestUnit: 'weeks' }); 14console.log(diff2.weeks, diff2.days); // 0, 5 15 16// Since vs until 17const daysSince = date2.since(date1); 18console.log(daysSince.days); // 5 19 20// With time precision 21const dt1 = Temporal.PlainDateTime.from('2024-03-15T10:00:00'); 22const dt2 = Temporal.PlainDateTime.from('2024-03-15T14:30:00'); 23const timeDiff = dt1.until(dt2); 24console.log(timeDiff.hours, timeDiff.minutes); // 4, 30

Duration#

1// Create duration 2const duration = Temporal.Duration.from({ 3 hours: 2, 4 minutes: 30, 5 seconds: 45, 6}); 7 8// From string (ISO 8601) 9const dur2 = Temporal.Duration.from('PT2H30M45S'); 10 11// Properties 12console.log(duration.hours); // 2 13console.log(duration.minutes); // 30 14console.log(duration.total('minutes')); // 150.75 15 16// Negate 17const negative = duration.negated(); 18 19// Absolute value 20const abs = negative.abs(); 21 22// Add durations 23const total = duration.add({ hours: 1 }); 24 25// Round duration 26const rounded = duration.round({ smallestUnit: 'minutes' });

Formatting#

1const date = Temporal.PlainDate.from('2024-03-15'); 2const dateTime = Temporal.PlainDateTime.from('2024-03-15T14:30:00'); 3 4// ISO strings 5console.log(date.toString()); // '2024-03-15' 6console.log(dateTime.toString()); // '2024-03-15T14:30:00' 7 8// With Intl.DateTimeFormat 9const formatter = new Intl.DateTimeFormat('en-US', { 10 weekday: 'long', 11 year: 'numeric', 12 month: 'long', 13 day: 'numeric', 14}); 15 16console.log(formatter.format(date)); 17// 'Friday, March 15, 2024' 18 19// Custom formatting 20function formatDate(date) { 21 return `${date.year}-${String(date.month).padStart(2, '0')}-${String(date.day).padStart(2, '0')}`; 22}

Calendar Support#

1// Different calendar systems 2const hebrewDate = Temporal.PlainDate.from({ 3 year: 5784, 4 month: 6, 5 day: 4, 6 calendar: 'hebrew', 7}); 8 9const islamicDate = Temporal.PlainDate.from({ 10 year: 1445, 11 month: 9, 12 day: 4, 13 calendar: 'islamic', 14}); 15 16// Convert between calendars 17const gregorianFromHebrew = hebrewDate.withCalendar('gregory'); 18 19// Get calendar info 20const date = Temporal.PlainDate.from('2024-03-15'); 21console.log(date.calendar.id); // 'iso8601' 22console.log(date.daysInMonth); // 31 23console.log(date.daysInYear); // 366 (leap year) 24console.log(date.monthsInYear); // 12 25console.log(date.inLeapYear); // true

Practical Examples#

1// Age calculation 2function calculateAge(birthDate) { 3 const today = Temporal.Now.plainDateISO(); 4 const birth = Temporal.PlainDate.from(birthDate); 5 const diff = birth.until(today, { largestUnit: 'years' }); 6 return diff.years; 7} 8 9console.log(calculateAge('1990-05-15')); // 33 (in 2024) 10 11// Business days between dates 12function businessDays(start, end) { 13 let count = 0; 14 let current = Temporal.PlainDate.from(start); 15 const endDate = Temporal.PlainDate.from(end); 16 17 while (Temporal.PlainDate.compare(current, endDate) < 0) { 18 const dayOfWeek = current.dayOfWeek; 19 if (dayOfWeek !== 6 && dayOfWeek !== 7) { 20 count++; 21 } 22 current = current.add({ days: 1 }); 23 } 24 25 return count; 26} 27 28// Meeting scheduler 29function scheduleRecurring(startDate, intervalDays, count) { 30 const meetings = []; 31 let current = Temporal.PlainDateTime.from(startDate); 32 33 for (let i = 0; i < count; i++) { 34 meetings.push(current.toString()); 35 current = current.add({ days: intervalDays }); 36 } 37 38 return meetings; 39}

Time Zones Handling#

1// Schedule across timezones 2function scheduleGlobalMeeting(baseTime, attendeeTimezones) { 3 const base = Temporal.ZonedDateTime.from(baseTime); 4 5 return attendeeTimezones.map((tz) => ({ 6 timezone: tz, 7 localTime: base.withTimeZone(tz).toString(), 8 })); 9} 10 11const times = scheduleGlobalMeeting( 12 '2024-03-15T10:00:00[America/New_York]', 13 ['America/Los_Angeles', 'Europe/London', 'Asia/Tokyo'] 14); 15 16// DST-safe scheduling 17function addWeeks(zonedDateTime, weeks) { 18 // This handles DST transitions correctly 19 return zonedDateTime.add({ weeks }); 20}

Migration from Date#

1// Convert Date to Temporal 2function dateToTemporal(date) { 3 return Temporal.Instant.fromEpochMilliseconds(date.getTime()); 4} 5 6// Convert Temporal to Date 7function temporalToDate(instant) { 8 return new Date(instant.epochMilliseconds); 9} 10 11// Wrapper for gradual migration 12class DateWrapper { 13 constructor(input) { 14 if (input instanceof Date) { 15 this.temporal = Temporal.Instant.fromEpochMilliseconds(input.getTime()); 16 } else { 17 this.temporal = Temporal.Instant.from(input); 18 } 19 } 20 21 toDate() { 22 return new Date(this.temporal.epochMilliseconds); 23 } 24 25 toZonedDateTime(timezone) { 26 return this.temporal.toZonedDateTimeISO(timezone); 27 } 28}

Best Practices#

Choose the Right Type: ✓ Instant for timestamps ✓ ZonedDateTime for user-facing times ✓ PlainDateTime for local events ✓ PlainDate for dates without time ✓ Duration for time spans Timezone Handling: ✓ Always store as UTC/Instant ✓ Convert to local for display ✓ Use named timezones, not offsets ✓ Handle DST transitions Comparison: ✓ Use compare() for sorting ✓ Use equals() for equality ✓ Use until()/since() for differences ✓ Specify largestUnit for clarity Avoid: ✗ Mixing Temporal and Date ✗ Storing local times as UTC ✗ Ignoring calendar systems ✗ Manual string formatting

Conclusion#

The Temporal API provides comprehensive date/time handling with proper timezone support, duration arithmetic, and calendar systems. Use Instant for timestamps, ZonedDateTime for timezone-aware times, and Plain* types for calendar dates and times. The API handles DST transitions, leap years, and calendar conversions correctly, making it far superior to the legacy Date object for modern JavaScript applications.

Share this article

Help spread the word about Bootspring