date-fns-tz Adapter Guide
The date-fns-tz adapter extends the date-fns adapter with comprehensive timezone support, providing a familiar API for date-fns users who need to work with multiple timezones.
Overview
The date-fns-tz adapter combines the lightweight, functional approach of date-fns with robust timezone handling capabilities. It's ideal for applications that need timezone support without the complexity of Luxon or the experimental nature of Temporal.
Key Features
- ✅ Full timezone support via IANA database
- ✅ DST (Daylight Saving Time) handling
- ✅ Extends familiar date-fns API
- ✅ Tree-shakeable and modular
- ✅ TypeScript support
- ✅ ~2.5KB gzipped (excluding peer dependencies)
Installation
bash
npm install @usetemporal/core date-fns date-fns-tzBasic Usage
typescript
import { createTemporal } from '@allystudio/usetemporal'
import { createDateFnsTzAdapter } from '@usetemporal/core/date-fns-tz'
// Create temporal with timezone support
const temporal = createTemporal({
adapter: createDateFnsTzAdapter({
timezone: 'America/New_York'
})
})
// All operations now respect the timezone
const now = usePeriod(temporal, 'day')
console.log(now.value.start) // Start of day in New York timeConfiguration Options
Timezone Configuration
typescript
// Fixed timezone
const nyTemporal = createTemporal({
adapter: createDateFnsTzAdapter({
timezone: 'America/New_York'
})
})
// User's local timezone
const localTemporal = createTemporal({
adapter: createDateFnsTzAdapter({
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
})
})
// UTC
const utcTemporal = createTemporal({
adapter: createDateFnsTzAdapter({
timezone: 'UTC'
})
})Locale Support
typescript
import { enUS, es, fr } from 'date-fns/locale'
const temporal = createTemporal({
adapter: createDateFnsTzAdapter({
timezone: 'Europe/Paris',
locale: fr // French locale
})
})Working with Timezones
Creating Periods in Different Timezones
typescript
// Create temporal instances for different timezones
const nyTemporal = createTemporal({
adapter: createDateFnsTzAdapter({ timezone: 'America/New_York' })
})
const tokyoTemporal = createTemporal({
adapter: createDateFnsTzAdapter({ timezone: 'Asia/Tokyo' })
})
// Same moment in time, different local representations
const utcDate = new Date('2025-07-25T12:00:00Z')
const nyPeriod = period(nyTemporal, utcDate, 'day')
const tokyoPeriod = period(tokyoTemporal, utcDate, 'day')
console.log(nyPeriod.start) // 2025-07-25 00:00:00 EDT
console.log(tokyoPeriod.start) // 2025-07-25 00:00:00 JSTTimezone Conversions
typescript
import { formatInTimeZone } from 'date-fns-tz'
const temporal = createTemporal({
adapter: createDateFnsTzAdapter({ timezone: 'America/New_York' })
})
const meeting = temporal.period( new Date(), 'hour')
// Display in different timezones
const nyTime = formatInTimeZone(meeting.start, 'America/New_York', 'PPpp')
const laTime = formatInTimeZone(meeting.start, 'America/Los_Angeles', 'PPpp')
const londonTime = formatInTimeZone(meeting.start, 'Europe/London', 'PPpp')Common Patterns
Business Hours Across Timezones
typescript
function getBusinessHours(temporal: Temporal, date: Date) {
const day = temporal.period( date, 'day')
const hours = divide(temporal.adapter, day, 'hour')
// Filter business hours (9 AM - 5 PM local time)
return hours.filter((hour, index) => index >= 9 && index < 17)
}
// Get business hours for different offices
const nyHours = getBusinessHours(nyTemporal, new Date())
const londonHours = getBusinessHours(londonTemporal, new Date())Scheduling Across Timezones
typescript
function scheduleGlobalMeeting(date: Date) {
const timezones = [
{ zone: 'America/New_York', label: 'New York' },
{ zone: 'Europe/London', label: 'London' },
{ zone: 'Asia/Tokyo', label: 'Tokyo' }
]
return timezones.map(({ zone, label }) => {
const temporal = createTemporal({
adapter: createDateFnsTzAdapter({ timezone: zone })
})
const meetingHour = temporal.period( date, 'hour')
return {
location: label,
localTime: formatInTimeZone(meetingHour.start, zone, 'PPpp'),
isBusinessHours: meetingHour.date.getHours() >= 9 &&
meetingHour.date.getHours() < 17
}
})
}DST Handling
typescript
// The adapter automatically handles DST transitions
const temporal = createTemporal({
adapter: createDateFnsTzAdapter({ timezone: 'America/New_York' })
})
// Spring forward (DST starts)
const beforeDST = new Date('2025-03-08T12:00:00')
const afterDST = new Date('2025-03-10T12:00:00')
const day1 = temporal.period( beforeDST, 'day')
const day2 = temporal.period( afterDST, 'day')
// day2 will be 23 hours long due to DSTPerformance Considerations
Caching Temporal Instances
typescript
// Cache temporal instances for frequently used timezones
const temporalCache = new Map()
function getTemporal(timezone: string) {
if (!temporalCache.has(timezone)) {
temporalCache.set(timezone, createTemporal({
adapter: createDateFnsTzAdapter({ timezone })
}))
}
return temporalCache.get(timezone)
}Batch Operations
typescript
// When working with multiple timezones, batch operations
const timezones = ['America/New_York', 'Europe/London', 'Asia/Tokyo']
const date = new Date()
// Efficient: Create all periods at once
const periods = timezones.map(tz => {
const temporal = getTemporal(tz)
return temporal.period( date, 'day')
})Integration with date-fns-tz
The adapter works seamlessly with date-fns-tz utilities:
typescript
import { zonedTimeToUtc, utcToZonedTime, format } from 'date-fns-tz'
const temporal = createTemporal({
adapter: createDateFnsTzAdapter({ timezone: 'America/New_York' })
})
const period = usePeriod(temporal, 'day')
// Convert to UTC
const utcDate = zonedTimeToUtc(period.value.start, 'America/New_York')
// Format with timezone
const formatted = format(period.value.start, 'yyyy-MM-dd HH:mm:ss zzz', {
timeZone: 'America/New_York'
})Migration from date-fns
If you're already using date-fns and need timezone support:
typescript
// Before: date-fns without timezone
import { startOfDay, addHours } from 'date-fns'
const start = startOfDay(new Date())
const later = addHours(start, 3)
// After: date-fns-tz adapter
import { createTemporal } from '@allystudio/usetemporal'
import { createDateFnsTzAdapter } from '@usetemporal/core/date-fns-tz'
const temporal = createTemporal({
adapter: createDateFnsTzAdapter({
timezone: 'America/New_York'
})
})
const day = temporal.period( new Date(), 'day')
const later = go(temporal.adapter, day, 3, 'hour')Comparison with Other Adapters
vs date-fns Adapter
- ✅ Adds timezone support
- ✅ Handles DST automatically
- ❌ Slightly larger bundle size
- ❌ Requires additional dependency
vs Luxon Adapter
- ✅ Lighter weight
- ✅ Familiar date-fns API
- ❌ Less comprehensive timezone features
- ❌ Fewer locale options
vs Temporal Adapter
- ✅ Stable and production-ready
- ✅ Well-documented
- ❌ Less precise for complex calculations
- ❌ Not a future standard
Best Practices
Choose the Right Timezone
typescript// User's timezone const userTz = Intl.DateTimeFormat().resolvedOptions().timeZone // Server timezone (usually UTC) const serverTz = 'UTC' // Specific business timezone const businessTz = 'America/New_York'Handle Timezone Changes
typescript// Allow users to change timezone function createUserTemporal(timezone: string) { return createTemporal({ adapter: createDateFnsTzAdapter({ timezone }) }) }Store UTC, Display Local
typescript// Store in UTC const utcDate = zonedTimeToUtc(localDate, userTimezone) // Display in user's timezone const localDate = utcToZonedTime(utcDate, userTimezone)
Common Issues
Invalid Timezone
typescript
// Handle invalid timezones gracefully
try {
const temporal = createTemporal({
adapter: createDateFnsTzAdapter({
timezone: 'Invalid/Timezone'
})
})
} catch (error) {
console.error('Invalid timezone, falling back to UTC')
const temporal = createTemporal({
adapter: createDateFnsTzAdapter({ timezone: 'UTC' })
})
}DST Ambiguity
typescript
// During DST transitions, some times occur twice
// The adapter handles this automatically, but be aware:
const ambiguousTime = new Date('2025-11-02T01:30:00')
// This could be 1:30 AM EDT or 1:30 AM ESTNext Steps
- Explore Timezone Best Practices
- Learn about date-fns-tz documentation
- See Performance Guide for optimization tips
- Review Migration Guide for switching from other libraries