Time zone support for date-fns v3 with the Intl API
npm install date-fns-tzdate-fns is a peer dependency of this library.
type property in your project's package.json to either module, for ESM, or commonjs.
date-fns will be used until they support
{"type": "module"}.
formatInTimeZone - Formats a date in the provided time zone,
fromZonedTime - Given a date and any time zone, returns a Date with the equivalent UTC time
toZonedTime - Get a date/time representing local time in a given time zone from the UTC date
getTimezoneOffset - Gets the offset in milliseconds between the time zone and UTC time
format - Extends date-fns/format with support for all time zone tokens,
z..zzzz
toDate - Can be used to parse a Date from a date string representing time in
America/New_York).
date-fns always returns a plain JS Date,
Date instance in the system's local time or an ISO8601 string, and
date-fns/format, and adds full support for:
z..zzz Unicode tokens: _short specific non-location format_, e.g. EST
zzzz Unicode token: _long specific non-location format_, e.g. Eastern Standard Time
date-fns/format, the z..zzzz, x..xxxxx, X..XXXXX and O..OOO tokens will all
Invalid Date passed to date-fns/format,
RangeError.
javascript
import { formatInTimeZone } from 'date-fns-tz'
const date = new Date('2014-10-25T10:46:20Z')
formatInTimeZone(date, 'America/New_York', 'yyyy-MM-dd HH:mm:ssXXX') // 2014-10-25 06:46:20-04:00
formatInTimeZone(date, 'America/New_York', 'yyyy-MM-dd HH:mm:ss zzz') // 2014-10-25 06:46:20 EST
formatInTimeZone(date, 'Europe/Paris', 'yyyy-MM-dd HH:mm:ss zzz') // 2014-10-25 12:46:20 GMT+2
// The time zone name is generated by the Intl API which works best when a locale is also provided
import { enGB } from 'date-fns/locale/en-GB'
formatInTimeZone(parisDate, 'Europe/Paris', 'yyyy-MM-dd HH:mm:ss zzz', { locale: enGB })
// 2014-10-25 10:46:20 CEST
formatInTimeZone(parisDate, 'Europe/Paris', 'yyyy-MM-dd HH:mm:ss zzzz', { locale: enGB })
// 2014-10-25 10:46:20 Central European Summer Time
`
Time zone offset helpers
These functions are useful when you are not formatting a date yourself, but passing it to
third-party code such as a date picker library alongside an input for selecting a time zone.
To discuss the usage of the time zone helpers let's assume we're writing a system where
administrators set up events which will start at a specific time in the venue's local time, and
this local time should be shown when accessing the site from anywhere in the world.
$3
Given a date and any time zone, returns the equivalent Date in the current system time zone
and the equivalent UTC time internally. An invalid date string or time zone will result in
an Invalid Date.
`ts
fromZonedTime(date: Date|Number|String, timeZone: String): Date
`
Say a user is asked to input the date/time and time zone of an event. A date/time picker will
typically return a Date instance with the chosen date, in the user's local time zone, and a
select input might provide the actual IANA time zone name.
In order to work with this info effectively it is necessary to find the equivalent UTC time:
`javascript
import { fromZonedTime } from 'date-fns-tz'
const date = getDatePickerValue() // e.g. 2014-06-25 10:00:00 (picked in any time zone)
const timeZone = getTimeZoneValue() // e.g. America/Los_Angeles
const utcDate = fromZonedTime(date, timeZone) // In June 10am in Los Angeles is 5pm UTC
postToServer(utcDate.toISOString(), timeZone) // post 2014-06-25T17:00:00.000Z, America/Los_Angeles
`
$3
Returns a Date which will format as the local time of any time zone from a specific UTC time
or date in the current system time zone. An invalid date string or time zone will result in
an Invalid Date.
`js
toZonedTime(date: Date|Number|String, timeZone: String): Date
`
Say the server provided a UTC date/time and a time zone which should be used as initial values
for the above form. The date/time picker will take a Date input which will be in the user's
local time zone, but the date value must be that of the target time zone.
`javascript
import { toZonedTime } from 'date-fns-tz'
const { isoDate, timeZone } = fetchInitialValues() // 2014-06-25T10:00:00.000Z, America/New_York
const date = toZonedTime(isoDate, timeZone) // In June 10am UTC is 6am in New York (-04:00)
renderDatePicker(date) // 2014-06-25 06:00:00 (in the system time zone)
renderTimeZoneSelect(timeZone) // America/New_York
`
$3
Returns the offset in milliseconds between the time zone and UTC time.
`js
getTimezoneOffset(timeZone: String, date: Date|Number): number
`
Returns the time zone offset from UTC time in milliseconds for IANA time zones as well
as other time zone offset string formats.
For time zones where daylight savings time is applicable a Date should be passed on
the second parameter to ensure the offset correctly accounts for DST at that time of
year. When omitted, the current date is used.
For invalid time zones, NaN is returned.
`javascript
import { getTimezoneOffset } from 'date-fns-tz'
const result = getTimezoneOffset('-07:00')
//=> -18000000 (-7 60 60 * 1000)
const result = getTimezoneOffset('Africa/Johannesburg')
//=> 7200000 (2 60 60 * 1000)
const result = getTimezoneOffset('America/New_York', new Date(2016, 0, 1))
//=> -18000000 (-5 60 60 * 1000)
const result = getTimezoneOffset('America/New_York', new Date(2016, 6, 1))
//=> -14400000 (-4 60 60 * 1000)
`
Low-level formatting helpers
$3
The format function exported from this library is used under the hood by formatInTimeZone
and extends date-fns/format with full time zone support for:
- The z..zzz Unicode tokens: _short specific non-location format_
- The zzzz Unicode token: _long specific non-location format_
When using those tokens with date-fns/format it falls back to the GMT time zone format, and
always uses the current system's local time zone. For example zzz in New York will always return
GMT-4 instead of the desired EST, and zzz in Paris GMT+2 instead of CEST, making the
time zone tokens somewhat irrelevant. This extended format function returns the proper
specific non-location format, e.g. EST or Eastern Standard Time, and that of the target time
zone (if provided, see below) rather than the system time zone.
Since a JavaScript Date instance cannot convey the time zone information to the format function
it is necessary to pass the timeZone value as an option on the third argument of format.
Similar to date-fns/format, when an invalid date is used a RangeError is thrown. When an invalid
time zone is provided _and included in the output_, i.e. with time zone tokens in the format
string, it will also throw a RangeError.
To format a date showing time for a specific time zone other than the system time zone, the
format function can be combined with toZonedTime. This is what formatInTimeZone does
internally. _To clarify, the format function will never change the underlying date, it must be
changed to a zoned time before passing it to format._
In most cases there is no need to use format rather than formatInTimeZone. The only time
this makes sense is when toZonedTime has been applied to a date once, and you want to
format it multiple times to different outputs.
`javascript
import { format, toZonedTime } from 'date-fns-tz'
const date = new Date('2014-10-25T10:46:20Z')
const nyDate = toZonedTime(date, 'America/New_York')
const parisDate = toZonedTime(date, 'Europe/Paris')
format(nyDate, 'yyyy-MM-dd HH:mm:ssXXX', { timeZone: 'America/New_York' }) // 2014-10-25 06:46:20-04:00
format(nyDate, 'yyyy-MM-dd HH:mm:ss zzz', { timeZone: 'America/New_York' }) // 2014-10-25 06:46:20 EST
format(parisDate, 'yyyy-MM-dd HH:mm:ss zzz', { timeZone: 'Europe/Paris' }) // 2014-10-25 10:46:20 GMT+2
// The time zone name is generated by the Intl API which works best when a locale is also provided
import { enGB } from 'date-fns/locale/en-GB'
format(parisDate, 'yyyy-MM-dd HH:mm:ss zzz', {
timeZone: 'Europe/Paris',
locale: enGB,
})
// 2014-10-25 10:46:20 CEST
format(parisDate, 'yyyy-MM-dd HH:mm:ss zzzz', {
timeZone: 'Europe/Paris',
locale: enGB,
})
// 2014-10-25 10:46:20 Central European Summer Time
`
$3
The toDate function can be used to parse a Date from a string containing a date and time
representing time in any time zone by providing an IANA time zone name on the timeZone option.
An invalid date string or time zone will result in an Invalid Date.
`javascript
import { toDate, format } from 'date-fns-tz'
// Offsets in the date string work as usual and take precedence
const parsedDate = toDate('2014-10-25T13:46:20+04:00')
const parisDate = toZonedTime(parsedDate, 'Europe/Paris')
format(parisDate, 'yyyy-MM-dd HH:mm:ssxxx', { timeZone: 'Europe/Paris' }) // 2014-10-25 11:46:20+02:00
// Since toDate simply clones a Date instance, the timeZone option is effectively ignored in this case
const date = new Date('2014-10-25T13:46:20Z')
const clonedDate = toDate(date, { timeZone: 'Europe/Paris' })
assert(date.valueOf() === clonedDate.valueOf())
// When there is no offset in the date string the timeZone property is used
const parsedDate = toDate('2014-10-25T13:46:20', { timeZone: 'Asia/Bangkok' })
const bangkokDate = toZonedTime(parsedDate, 'Asia/Bangkok')
format(bangkokDate, 'yyyy-MM-dd HH:mm:ssxxx', { timeZone: 'Asia/Bangkok' }) // 2014-10-25 13:46:20+07:00
`
Usage with Android
This library works with React Native on iOS, and on Android with Hermes which supports
Intl by default.
In Android projects that do not use Hermes, make this change in android/app/build.gradle:
`diff
- def jscFlavor = 'org.webkit:android-jsc:+'
+ def jscFlavor = 'org.webkit:android-jsc-intl:+'
`
Usage with Node.js
Node.js supports the Intl` API and ships with full ICU data included in the binary since v13,