A Temporal.ZonedDateTime
is a timezone-aware, calendar-aware date/time type that represents a real event that has happened (or will happen) at a particular instant from the perspective of a particular region on Earth.
As the broadest Temporal
type, Temporal.ZonedDateTime
can be considered a combination of Temporal.Instant
, Temporal.PlainDateTime
, and a time zone.
As the only Temporal
type that persists a time zone, Temporal.ZonedDateTime
is optimized for use cases that require a time zone:
Temporal.PlainDateTime
) and the UTC timeline (like Temporal.Instant
) without having to do any work to keep the two in sync.A Temporal.ZonedDateTime
instance can be losslessly converted into every other Temporal
type except Temporal.Duration
.
Temporal.Instant
, Temporal.PlainDateTime
, Temporal.PlainDate
, Temporal.PlainTime
, Temporal.PlainYearMonth
, and Temporal.PlainMonthDay
all carry less information and can be used when complete information is not required.
The Temporal.ZonedDateTime
API is a superset of Temporal.PlainDateTime
, which makes it easy to port code back and forth between the two types as needed. Because Temporal.PlainDateTime
is not aware of time zones, in use cases where the time zone is known it's recommended to use Temporal.ZonedDateTime
which will automatically adjust for DST and can convert easily to Temporal.Instant
without having to re-specify the time zone.
Time zones in Temporal
are represented by string identifiers from the IANA Time Zone Database (like Asia/Tokyo
, America/Los_Angeles
, or UTC
) or by a fixed UTC offset like +05:30
.
For example:
inBerlin = Temporal.ZonedDateTime.from('2022-01-28T19:53+01:00[Europe/Berlin]');
inTokyo = inBerlin.withTimeZone('Asia/Tokyo');
Time zone identifiers are occasionally renamed or merged in the IANA Time Zone Database.
For example, Asia/Calcutta
was renamed to Asia/Kolkata
, and America/Montreal
was merged into America/Toronto
because both identifiers are in the same country and share the same time zone rules since 1970.
Identifiers that have been renamed or merged are considered equivalent by ECMAScript.
Equivalence can be tested using Temporal.ZonedDateTime.prototype.equals
.
function areTimeZoneIdentifiersEquivalent(id1, id2) {
return new Temporal.ZonedDateTime(0n, id1).equals(new Temporal.ZonedDateTime(0n, id2));
// DON'T DO THIS: return id1 === id2;
}
areTimeZoneIdentifiersEquivalent('Asia/Calcutta', 'ASIA/KOLKATA'); // => true
areTimeZoneIdentifiersEquivalent('Asia/Calcutta', '+05:30'); // => false
areTimeZoneIdentifiersEquivalent('UTC', '+00:00'); // => false
Time zones that resolve to different Zones in the IANA Time Zone Database are not equivalent, even if those Zones use the same offsets. Similarly, a numeric-offset identifier is never equivalent to an IANA time zone identifier, even if they always represent the same offset.
In any set of equivalent identifiers, only one identifier will be considered canonical.
To avoid redundancy, the output of Intl.supportedValuesOf('timeZone')
and Temporal.Now.timeZoneId()
are limited to canonical identifiers.
Other than those cases, canonicalization is not observable in ECMAScript code, which ensures that changes to the IANA Time Zone Database will have minimal impact on the behavior of existing applications.
The IANA Time Zone Database can be built with different options that can change which time zones are equivalent. ECMAScript implementations generally use build options that guarantee at least one canonical identifier for every ISO 3166-1 Alpha-2 country code, and that ensure that identifiers for different country codes are never equivalent. This behavior avoids the risk that future political changes in one country can affect the behavior of ECMAScript code using a different country's time zones.
For example, the default build options consider Europe/Oslo, Europe/Stockholm, Europe/Copenhagen, and Europe/Berlin to be equivalent. However, ECMAScript implementations generally do not treat those as equivalent.
Parameters:
epochNanoseconds
(bigint): A number of nanoseconds.timeZone
(string): The time zone in which the event takes place.calendar
(optional string): Calendar used to interpret dates and times.Returns: a new Temporal.ZonedDateTime
object.
Like all Temporal
constructors, this constructor is an advanced API used to create instances for a narrow set of use cases.
Instead of the constructor, Temporal.ZonedDateTime.from()
is preferred instead because it accepts more kinds of input and provides options for handling ambiguity and overflow.
The range of allowed values for this type is the same as the old-style JavaScript Date
: 100 million (108) days before or after the Unix epoch.
This range covers approximately half a million years. If epochNanoseconds
is outside of this range, a RangeError
will be thrown.
timeZone
is a string containing the identifier of a built-in time zone, such as 'UTC'
, 'Europe/Madrid'
, or '+05:30'
.
calendar
is a string containing the identifier of a built-in calendar, such as 'islamic'
or 'gregory'
.
If omitted, the ISO 8601 calendar will be used, which is identical to the Gregorian calendar except negative years are used instead of eras like BC/BCE or AD/CE.
Usage examples:
// UNIX epoch in California
new Temporal.ZonedDateTime(0n, 'America/Los_Angeles', 'iso8601');
// => 1969-12-31T16:00:00-08:00[America/Los_Angeles]
new Temporal.ZonedDateTime(0n, 'America/Los_Angeles');
// => 1969-12-31T16:00:00-08:00[America/Los_Angeles]
// same, but shorter
Parameters:
item
: a value convertible to a Temporal.ZonedDateTime
.options
(optional object): An object which may have some or all of the following properties:overflow
(string): How to deal with out-of-range values if item
is an object.
Allowed values are 'constrain'
and 'reject'
.
The default is 'constrain'
.disambiguation
(string): How to disambiguate if the date and time given by zonedDateTime
does not exist in the time zone, or exists more than once.
Allowed values are 'compatible'
, 'earlier'
, 'later'
, and 'reject'
.
The default is 'compatible'
.offset
(string): How to interpret a provided time zone offset (e.g. -02:00
) if it conflicts with the provided time zone (e.g. America/Sao_Paulo
).
Allowed values are 'use'
, 'ignore'
, 'prefer'
, and 'reject'
.
The default is 'reject'
.Returns: a new Temporal.ZonedDateTime
object.
This static method creates a new Temporal.ZonedDateTime
object from another value.
If the value is another Temporal.ZonedDateTime
object, a new but otherwise identical object will be returned.
If the value is any other object, a Temporal.ZonedDateTime
will be constructed from the values of any timeZone
, year
(or era
and eraYear
), month
(or monthCode
), day
, hour
, minute
, second
, millisecond
, microsecond
, nanosecond
, and/or calendar
properties that are present.
At least the timeZone
, year
(or era
and eraYear
), month
(or monthCode
), and day
properties must be present. Other properties are optional.
If calendar
is missing, it will be assumed to be 'iso8601'
(identifying the ISO 8601 calendar).
Any other missing properties will be assumed to be 0 (for time fields).
Date/time values will be interpreted in context of the provided offset and/or time zone, depending on the offset
option.
Date/time values in object inputs will be interpreted in the context of calendar
.
However, date/time values in string inputs are always interpreted in the context of the ISO 8601 calendar.
If the value is not an object, it must be a string, which is expected to be an ISO 8601 string that includes a time zone ID in brackets, and an optional calendar. For example:
2020-08-05T20:06:13+09:00[Asia/Tokyo][u-ca=japanese]
If the string isn't valid, then a RangeError
will be thrown regardless of the value of overflow
.
Note that this string format (albeit limited to the ISO 8601 calendar system) is also used by java.time
and some other time-zone-aware libraries.
For more information on Temporal
's extensions to the ISO 8601 / RFC 3339 string format and the progress towards becoming a published standard, see String Parsing, Serialization, and Formatting.
The time zone ID is always required.
2020-08-05T20:06:13+09:00
and 2020-08-05T11:06:13Z
are not valid inputs to this method because they don't include a time zone ID in square brackets.
To parse these string formats, use Temporal.Instant
:
Temporal.Instant.from('2020-08-05T20:06:13+0900').toZonedDateTimeISO('Asia/Tokyo');
Usually a named IANA time zone like Europe/Paris
or America/Los_Angeles
is used, but there are cases where adjusting for DST or other time zone offset changes is not desired.
For these cases, non-DST-adjusting, single-offset time zones are available, e.g. Etc/GMT-14
through Etc/GMT+12
.
For historical reasons, signs are reversed between these time zones' names and their offsets.
For example, Etc/GMT+8
would be used for cases where the UTC offset is always -08:00
, e.g. ocean shipping off the coast of California.
If a non-whole-hour single-offset time zone is needed, the offset can be used as the time zone ID of an offset time zone.
Temporal.ZonedDateTime.from('2020-08-05T20:06:13+05:45[+05:45]');
// OR
Temporal.Instant.from('2020-08-05T20:06:13+05:45').toZonedDateTimeISO('+05:45');
// => 2020-08-05T20:06:13+05:45[+05:45]
Note that using Temporal.ZonedDateTime
with a single-offset time zone will not adjust for Daylight Saving Time or other time zone changes.
Therefore, using offset time zones with Temporal.ZonedDateTime
is relatively unusual.
Instead of using Temporal.ZonedDateTime
with an offset time zone, it may be easier for most use cases to use Temporal.PlainDateTime
and/or Temporal.Instant
instead.
The overflow
option works as follows, if item
is an object:
'constrain'
mode (the default), any out-of-range values are clamped to the nearest in-range value (after assuming extension of eras over arbitrary years to substitute era
and eraYear
with appropriate values for the item
).'reject'
mode, the presence of out-of-range values (after assuming extension of eras over arbitrary years to substitute era
and eraYear
with appropriate values for the item
) will cause the function to throw a RangeError
.The overflow
option is ignored if item
is a string.
Additionally, if the result is earlier or later than the range of dates that Temporal.PlainDateTime
can represent (approximately half a million years centered on the Unix epoch), then this method will throw a RangeError
regardless of overflow
.
NOTE: Although Temporal does not deal with leap seconds, dates coming from other software may have a
second
value of 60. In the default'constrain'
mode and when parsing an ISO 8601 string, this will be converted to 59. In'reject'
mode, this function will throw, so if you have to interoperate with times that may contain leap seconds, don't use'reject'
.
If the input contains a time zone offset, in rare cases it's possible for those values to conflict for a particular local date and time.
For example, this could happen if the definition of a time zone is changed (e.g. to abolish DST) after storing a Temporal.ZonedDateTime
as a string representing a far-future event.
If the time zone and offset are in conflict, then the offset
option is used to resolve the conflict:
'use'
: Evaluate date/time values using the time zone offset if it's provided in the input.
This will keep the exact time unchanged even if local time will be different than what was originally stored.'ignore'
: Never use the time zone offset provided in the input. Instead, calculate the offset from the time zone.
This will keep local time unchanged but may result in a different exact time than was originally stored.'prefer'
: Evaluate date/time values using the offset if it's valid for this time zone.
If the offset is invalid, then calculate the offset from the time zone.
This option is rarely used when calling from()
.
See the documentation of with()
for more details about why this option is used.'reject'
: Throw a RangeError
if the offset is not valid for the provided date and time in the provided time zone.An example of why offset
is needed is Brazil's abolition of DST in 2019.
This change meant that previously-stored values for 2020 and beyond might now be ambiguous.
For details about problems like this and how to solve them with offset
, see Ambiguity Caused by Permanent Changes to a Time Zone Definition.
The offset
option is ignored if an offset is not present in the input.
In that case, the time zone and the disambiguation
option are used to convert date/time values to exact time.
The disambiguation
option controls what time zone offset is used when the input time is ambiguous (as in the repeated clock hour after DST ends) or invalid due to offset changes skipping clock time (as in the skipped clock hour after DST starts):
'compatible'
(the default): Acts like 'earlier'
for backward transitions and 'later'
for forward transitions.'earlier'
: The earlier of two possible times.'later'
: The later of two possible times.'reject'
: Throw a RangeError
instead.When interoperating with existing code or services, 'compatible'
mode matches the behavior of legacy Date
as well as libraries like moment.js, Luxon, and date-fns.
This mode also matches the behavior of cross-platform standards like RFC 5545 (iCalendar).
During "skipped" clock time like the hour after DST starts, this method interprets invalid times using the pre-transition time zone offset if 'compatible'
or 'later'
is used or the post-transition time zone offset if 'earlier'
is used.
This behavior avoids exceptions when converting nonexistent local time values to Temporal.ZonedDateTime
.
For usage examples and a more complete explanation of how this disambiguation works and why it is necessary, see Time Zones and Resolving Ambiguity.
The disambiguation
option is only used if there is no offset in the input, or if the offset is ignored by using the offset
option as described above.
If the offset in the input is used, then there is no ambiguity and the disambiguation
option is ignored.
NOTE: The allowed values for the
item.month
property start at 1, which is different from legacyDate
where months are represented by zero-based indices (0 to 11).
Example usage:
zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24:30+02:00[Africa/Cairo]');
zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24:30+02:00[Africa/Cairo][u-ca=islamic]');
zdt = Temporal.ZonedDateTime.from('19951207T032430+0200[Africa/Cairo]');
/* WRONG */ zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24:30'); // => throws RangeError: time zone ID required
/* WRONG */ zdt = Temporal.ZonedDateTime.from('1995-12-07T01:24:30Z'); // => throws RangeError: time zone ID required
/* WRONG */ zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24:30+02:00'); // => throws RangeError: time zone ID required
zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24:30+02:00[+02:00]'); // OK (offset time zone) but rarely used
/* WRONG */ zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24:30+03:00[Africa/Cairo]');
// => RangeError: Offset is invalid for '1995-12-07T03:24:30' in 'Africa/Cairo'. Provided: +03:00, expected: +02:00.
zdt = Temporal.ZonedDateTime.from({
timeZone: 'America/Los_Angeles',
year: 1995,
month: 12,
day: 7,
hour: 3,
minute: 24,
second: 30,
millisecond: 0,
microsecond: 3,
nanosecond: 500
}); // => 1995-12-07T03:24:30.0000035-08:00[America/Los_Angeles]
// Different overflow modes
zdt = Temporal.ZonedDateTime.from({ timeZone: 'Europe/Paris', year: 2001, month: 13, day: 1 }, { overflow: 'constrain' })
// => 2001-12-01T00:00:00+01:00[Europe/Paris]
zdt = Temporal.ZonedDateTime.from({ timeZone: 'Europe/Paris', year: 2001, month: 13, day: 1 }, { overflow: 'reject' })
// => throws RangeError
Parameters:
one
(Temporal.ZonedDateTime
): First value to compare.two
(Temporal.ZonedDateTime
): Second value to compare.Returns: an integer indicating whether one
comes before or after or is equal to two
.
one
is less than two
one
is greater than two
Comparison uses exact time, not calendar date and clock time, because sorting is almost always based on when events happen in the real world (and note that sorting by clock time may not match the order of actual occurrence near discontinuities such as DST transitions).
Calendars and time zones are also ignored in the comparison for the same reason.
For example, this method returns 0
for instances that fall on the same date and time in the ISO 8601 calendar and UTC time zone, even if fields like day
or hour
do not match due to use of different calendars and/or time zones.
This function can be used to sort arrays of Temporal.ZonedDateTime
objects.
For example:
arr = [
Temporal.ZonedDateTime.from('2020-02-01T12:30-05:00[America/Toronto]'),
Temporal.ZonedDateTime.from('2020-02-01T12:30-05:00[America/New_York]'),
Temporal.ZonedDateTime.from('2020-02-01T12:30+01:00[Europe/Brussels]'),
Temporal.ZonedDateTime.from('2020-02-01T12:30+00:00[Europe/London]')
];
sorted = arr.sort(Temporal.ZonedDateTime.compare);
JSON.stringify(sorted, undefined, 2);
// =>
// '[
// "2020-02-01T12:30+01:00[Europe/Brussels]",
// "2020-02-01T12:30+00:00[Europe/London]",
// "2020-02-01T12:30-05:00[America/Toronto]",
// "2020-02-01T12:30-05:00[America/New_York]"
// ]'
Note that in unusual cases like the repeated clock hour after DST ends, values that are later in the real world can be earlier in clock time, or vice versa.
To sort Temporal.ZonedDateTime
values according to clock time only (which is a very rare use case), convert each value to Temporal.PlainDateTime
.
For example:
one = Temporal.ZonedDateTime.from('2020-11-01T01:45-07:00[America/Los_Angeles]');
two = Temporal.ZonedDateTime.from('2020-11-01T01:15-08:00[America/Los_Angeles]');
Temporal.ZonedDateTime.compare(one, two);
// => -1
// (because `one` is earlier in the real world)
Temporal.PlainDateTime.compare(one.toPlainDateTime(), two.toPlainDateTime());
// => 1
// (because `one` is later in clock time)
Temporal.Instant.compare(one.toInstant(), two.toInstant());
// => -1
// (because `Temporal.Instant` and `Temporal.ZonedDateTime` both compare real-world exact times)
The above read-only properties allow accessing each component of a date or time individually.
Date unit details:
year
is a signed integer representing the number of years relative to a calendar-specific epoch.
For calendars that use eras, the anchor is usually aligned with the latest era so that eraYear === year
for all dates in that era.
However, some calendars use a different anchor (e.g., the Japanese calendar year
matches the ISO 8601 and Gregorian calendars in counting from ISO year 0001 as 1
).month
is a positive integer representing the ordinal index of the month in the current year.
For calendars like Hebrew or Chinese that use leap months, the same-named month may have a different month
value depending on the year.
The first month in every year has month
equal to 1
.
The last month of every year has month
equal to the monthsInYear
property.
month
values start at 1, which is different from legacy Date
where months are represented by zero-based indices (0 to 11).monthCode
is a calendar-specific string that identifies the month in a year-independent way.
For common (non-leap) months, monthCode
should be `M${month}`
, where month
is zero padded up to two digits.
For uncommon (leap) months in lunisolar calendars like Hebrew or Chinese, the month code is the previous month's code with an "L" suffix appended.
Examples: 'M02'
=> February; 'M08L'
=> repeated 8th month in the Chinese calendar; 'M05L'
=> Adar I in the Hebrew calendar.day
is a positive integer representing the day of the month.Either month
or monthCode
can be used in from
or with
to refer to the month.
Similarly, in calendars that use eras, an era
/eraYear
pair can be used in place of year
when calling from
or with
.
Time unit details:
hour
is an integer between 0 and 23minute
is an integer between 0 and 59second
is an integer between 0 and 59.
If 60 (for a leap second) was provided to from
or with
, 59 will stored and will be returned by this property.millisecond
is an integer between 0 and 999microsecond
is an integer between 0 and 999nanosecond
is an integer between 0 and 999Usage examples:
dt = Temporal.ZonedDateTime.from('1995-12-07T03:24:30.000003500[Europe/Rome]');
dt.year; // => 1995
dt.month; // => 12
dt.monthCode; // => 'M12'
dt.day; // => 7
dt.hour; // => 3
dt.minute; // => 24
dt.second; // => 30
dt.millisecond; // => 0
dt.microsecond; // => 3
dt.nanosecond; // => 500
dt = Temporal.ZonedDateTime.from('2019-02-23T03:24:30.000003500[Europe/Rome][u-ca=hebrew]');
dt.year; // => 5779
dt.month; // => 6
dt.monthCode; // => 'M05L'
dt.day; // => 18
dt.hour; // => 3
dt.minute; // => 24
dt.second; // => 30
dt.millisecond; // => 0
dt.microsecond; // => 3
dt.nanosecond; // => 500
NOTE: The possible values for the
month
property start at 1, which is different from legacyDate
where months are represented by zero-based indices (0 to 11).
The above two read-only properties give the integer number of milliseconds or nanoseconds (respectively) from the Unix epoch of January 1, 1970 at 00:00 UTC until zonedDateTime
, ignoring leap seconds.
These properties are equivalent to zonedDateTime.toInstant().epochMilliseconds
and zonedDateTime.toInstant().epochNanoseconds
, respectively.
Any fractional milliseconds are truncated towards the beginning of time.
The time zone is irrelevant to these properties, because there is only one epoch, not one per time zone.
Note that the epochMilliseconds
property is of type number
(although only integers are returned) while the epochNanoseconds
property is of type bigint
.
The epochMilliseconds
property is the easiest way to construct a legacy Date
object from a Temporal.ZonedDateTime
instance.
zdt = Temporal.ZonedDateTime.from('2020-02-01T12:30+09:00[Asia/Tokyo]');
epochMs = zdt.epochMilliseconds;
// => 1580527800000
zdt.toInstant().epochMilliseconds;
// => 1580527800000
legacyDate = new Date(epochMs);
// => 2020-02-01T03:30:00.000Z
// (if the system time zone is America/Los_Angeles)
epochNanos = zdt.epochNanoseconds;
// => 1580527800000000000n
// If you need epoch seconds data:
epochSecs = Math.floor(zdt.epochMillieconds / 1000); // => 1553906700
// => 1580527800
// If you need epoch microseconds data:
// (Note the extra check for correct floor rounding with bigints)
ns = zdt.epochNanoseconds;
epochMicros = ns / 1000n + ((ns % 1000n) < 0n ? -1n : 0n);
// => 1580527800000000n
The calendarId
read-only property gives the identifier of the calendar used to calculate date/time field values.
Calendar-sensitive values are used in most places, including:
.year
or .month
.from()
or .with()
.Temporal.Duration
instances with .since()
Temporal.Duration
instances with .add()
or .subtract()
toLocaleString()
, although if the calendar is ISO then the calendar can be overridden via an optionCalendar-specific date/time values are NOT used in only a few places:
.toString()
, because ISO-string date/time values are, by definition, using the ISO 8601 calendargetISOFields()
method which is explicitly used to provide ISO 8601 calendar valuesTemporal.ZonedDateTime
constructor which is used for advanced use cases onlyThe timeZoneId
read-only property is the identifier of the persistent time zone of zonedDateTime
.
By storing its time zone, Temporal.ZonedDateTime
is able to use that time zone when deriving other values, e.g. to automatically perform DST adjustment when adding or subtracting time.
Usually, the time zone ID will be an IANA time zone ID.
However, in unusual cases, a time zone can also be created from a time zone offset string like +05:30
.
Offset time zones function just like IANA time zones except that their offset can never change due to DST or political changes.
This can be problematic for many use cases because by using an offset time zone you lose the ability to safely derive past or future dates because, even in time zones without DST, offsets sometimes change for political reasons (e.g. countries change their time zone).
Therefore, using an IANA time zone is recommended wherever possible.
Time zone identifiers are normalized before being used to determine the time zone.
For example, capitalization will be corrected to match the IANA time zone database, and offsets like +01
or +0100
will be converted to +01:00
.
Link names in the IANA Time Zone Database are not resolved to Zone names.
In very rare cases, you may choose to use UTC
as your time zone ID.
This is generally not advised because no humans actually live in the UTC time zone; it's just for computers.
Also, UTC has no DST and always has a zero offset, which means that any action you'd take with Temporal.ZonedDateTime
would return identical results to the same action on Temporal.PlainDateTime
or Temporal.Instant
.
Therefore, you should almost always use Temporal.Instant
to represent UTC times.
When you want to convert UTC time to a real time zone, that's when Temporal.ZonedDateTime
will be useful.
To change the time zone while keeping the exact time constant, use .withTimeZone(timeZone)
.
The time zone is a required property when creating Temporal.ZonedDateTime
instances.
If you don't know the time zone of your underlying data, please use Temporal.Instant
and/or Temporal.PlainDateTime
, neither of which have awareness of time zones.
Usage example:
zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24-08:00[America/Los_Angeles]');
`Time zone is: ${zdt.timeZoneId}`;
// => 'Time zone is: America/Los_Angeles'
zdt.withTimeZone('Asia/Kolkata').timeZoneId;
// => Asia/Kolkata
zdt.withTimeZone('Asia/Calcutta').timeZoneId;
// => Asia/Calcutta (does not follow links in the IANA Time Zone Database)
zdt.withTimeZone('europe/paris').timeZoneId;
// => Europe/Paris (normalized to match IANA Time Zone Database capitalization)
zdt.withTimeZone('+05:00').timeZoneId;
// => +05:00
zdt.withTimeZone('+05').timeZoneId;
// => +05:00 (normalized to ±HH:MM)
zdt.withTimeZone('+0500').timeZoneId;
// => +05:00 (normalized to ±HH:MM)
In calendars that use eras, the era
and eraYear
read-only properties can be used together to resolve an era-relative year.
Both properties are undefined
when using the ISO 8601 calendar.
As inputs to from
or with
, era
and eraYear
can be used instead of year
.
Unlike year
, eraYear
may decrease as time proceeds because some eras (like the BCE era in the Gregorian calendar) count years backwards.
date = Temporal.ZonedDateTime.from('-000015-01-01T12:30[Europe/Rome][u-ca=gregory]');
date.era;
// => 'bce'
date.eraYear;
// => 16
date.year;
// => -15
The dayOfWeek
read-only property gives the weekday number that the date falls on.
For the ISO 8601 calendar, the weekday number is defined as in the ISO 8601 standard: a value between 1 and 7, inclusive, with Monday being 1, and Sunday 7.
For an overview, see ISO 8601 on Wikipedia.
Usage example:
zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24-08:00[America/Los_Angeles]');
['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'][zdt.dayOfWeek - 1]; // => 'THU'
The dayOfYear
read-only property gives the ordinal day of the year that the date falls on.
For the ISO 8601 calendar, this is a value between 1 and 365, or 366 in a leap year.
Usage example:
zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24-08:00[America/Los_Angeles]');
// ISO ordinal date
console.log(zdt.year, zdt.dayOfYear); // => '1995 341'
The weekOfYear
read-only property gives the ISO week number of the date.
For the ISO 8601 calendar, this is normally a value between 1 and 52, but in a few cases it can be 53 as well.
ISO week 1 is the week containing the first Thursday of the year.
For more information on ISO week numbers, see for example the Wikipedia article on ISO week date.
When combining the week number with a year number, make sure to use zonedDateTime.yearOfWeek
instead of zonedDateTime.year
.
This is because the first few days of a calendar year may be part of the last week of the previous year, and the last few days of a calendar year may be part of the first week of the new year, depending on which year the first Thursday falls in.
Usage example:
zdt = Temporal.ZonedDateTime.from('2022-01-01T03:24-08:00[America/Los_Angeles]');
// ISO week date
console.log(zdt.yearOfWeek, zdt.weekOfYear, zdt.dayOfWeek); // => '2021 52 6'
The yearOfWeek
read-only property gives the ISO "week calendar year" of the date, which is the year number corresponding to the ISO week number.
For the ISO 8601 calendar, this is normally the same as zonedDateTime.year
, but in a few cases it may be the previous or following year.
For more information on ISO week numbers, see for example the Wikipedia article on ISO week date.
See weekOfYear
for a usage example.
The daysInWeek
read-only property gives the number of days in the week that the date falls in.
For the ISO 8601 calendar, this is always 7, but in other calendar systems it may differ from week to week.
Usage example:
zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24-08:00[America/Los_Angeles]');
zdt.daysInWeek; // => 7
The daysInMonth
read-only property gives the number of days in the month that the date falls in.
For the ISO 8601 calendar, this is 28, 29, 30, or 31, depending on the month and whether the year is a leap year.
Usage example:
// Attempt to write some mnemonic poetry
const monthsByDays = {};
for (let month = 1; month <= 12; month++) {
const zdt = Temporal.Now.zonedDateTimeISO().with({ month });
monthsByDays[zdt.daysInMonth] = (monthsByDays[zdt.daysInMonth] || []).concat(zdt);
}
const strings = monthsByDays[30].map((zdt) => zdt.toLocaleString('en', { month: 'long' }));
// Shuffle to improve poem as determined empirically
strings.unshift(strings.pop());
const format = new Intl.ListFormat('en');
const poem = `Thirty days hath ${format.format(strings)}`;
console.log(poem);
The daysInYear
read-only property gives the number of days in the year that the date falls in.
For the ISO 8601 calendar, this is 365 or 366, depending on whether the year is a leap year.
Usage example:
zdt = Temporal.Now.zonedDateTimeISO();
percent = zdt.dayOfYear / zdt.daysInYear;
`The year is ${percent.toLocaleString('en', { style: 'percent' })} over!`;
// example output: "The year is 10% over!"
The monthsInYear
read-only property gives the number of months in the year that the date falls in.
For the ISO 8601 calendar, this is always 12, but in other calendar systems it may differ from year to year.
Usage example:
zdt = Temporal.ZonedDateTime.from('1900-01-01T12:00+09:00[Asia/Tokyo]');
zdt.monthsInYear; // => 12
The inLeapYear
read-only property tells whether the year of this Temporal.ZonedDateTime
is a leap year.
Its value is true
if the year is a leap year, and false
if not.
NOTE: A "leap year" is a year that contains more days than other years (for solar or lunar calendars) or more months than other years (for lunisolar calendars like Hebrew or Chinese). In the ISO 8601 calendar, a year is a leap year (and has exactly one extra day, February 29) if it is evenly divisible by 4 but not 100 or if it is evenly divisible by 400.
Usage example:
// Is this year a leap year?
zdt = Temporal.Now.zonedDateTimeISO();
zdt.inLeapYear; // example output: true
// Is 2100 a leap year? (no, because it's divisible by 100 and not 400)
zdt.with({ year: 2100 }).inLeapYear; // => false
The hoursInDay
read-only property returns the number of real-world hours between the start of the current day (usually midnight) in zonedDateTime.timeZone
to the start of the next calendar day in the same time zone.
Normally days will be 24 hours long, but on days where there are DST changes or other time zone transitions, this property may return 23 or 25.
In rare cases, other integers or even non-integer values may be returned, e.g. when time zone definitions change by less than one hour.
If a time zone offset transition happens exactly at midnight, the transition will impact the previous day's length.
Note that transitions that skip entire days (like the 2011 change of Pacific/Apia
to the opposite side of the International Date Line) will return 24
because there are 24 real-world hours between one day's midnight and the next day's midnight.
When the same day starts twice (due to an offset transition), hoursInDay
reflects the total amount of time between the first start of the day, and the second end of the day. For example, on 2010-11-07 at 00:00:59 (1 minute after midnight), the America/St_Johns
time zone transitioned from offset -02:30
to offset -03:30
, meaning that the time transitioned to 23:01:00 on the previous day. After 1 hour of wall-clock time passed, 2010-11-07 began again:
const zdt = Temporal.ZonedDateTime.from('2010-11-07T23:00:00-03:30[America/St_Johns]);
zdt.hoursInDay; // 25
Similar examples include America/Goose_Bay
before 2010, America/Moncton
before 2006, Pacific/Guam
and Pacific/Saipan
in 1969, and America/Phoenix
in 1944.
Usage example:
Temporal.ZonedDateTime.from('2020-01-01T12:00-08:00[America/Los_Angeles]').hoursInDay;
// => 24
// (normal day)
Temporal.ZonedDateTime.from('2020-03-08T12:00-07:00[America/Los_Angeles]').hoursInDay;
// => 23
// (DST starts on this day)
Temporal.ZonedDateTime.from('2020-11-01T12:00-08:00[America/Los_Angeles]').hoursInDay;
// => 25
// (DST ends on this day)
The offsetNanoseconds
read-only property is the offset (in nanoseconds) relative to UTC of zonedDateTime
.
The value of this field will change after DST transitions or after political changes to a time zone, e.g. a country switching to a new time zone.
To change the offset using with
(or from
using an property bag object instead of a string), use the string-typed offset
field.
The numeric offsetNanoseconds
field is read-only and is ignored in with
and from
.
zdt = Temporal.ZonedDateTime.from('2020-11-01T01:30-07:00[America/Los_Angeles]');
zdt.offsetNanoseconds;
// => -25200000000000
// (-7 * 3600 * 1e9)
The offset
read-only property is the offset (formatted as a string) relative to UTC of the current time zone and exact instant. Examples: '-08:00'
or '+05:30'
The format used is defined in the ISO 8601 standard.
The value of this field will change after DST transitions or after political changes to a time zone, e.g. a country switching to a new time zone.
This field is used to uniquely map date/time fields to an exact date/time in cases where the calendar date and clock time are ambiguous due to time zone offset transitions.
Therefore, this field is accepted by from
and with
.
The presence of this field means that zonedDateTime.toInstant()
requires no parameters.
zdt = Temporal.ZonedDateTime.from('2020-11-01T01:30-07:00[America/Los_Angeles]');
zdt.offset;
// => '-07:00'
zdt.withTimeZone('Asia/Kolkata').offset;
// => '+05:30'
minus8Hours = '-08:00';
daylightTime0130 = Temporal.ZonedDateTime.from('2020-11-01T01:30-07:00[America/Los_Angeles]');
// => 2020-11-01T01:30:00-07:00[America/Los_Angeles]
// This is Pacific Daylight Time 1:30AM
repeated0130 = daylightTime0130.with({ offset: minus8Hours });
// => 2020-11-01T01:30:00-08:00[America/Los_Angeles]
// This is Pacific Standard Time 1:30AM
Parameters:
zonedDateTimeLike
(object): an object with some of the properties of a Temporal.ZonedDateTime
(including offset
, but not timeZone
or calendar
).options
(optional object): An object which may have some or all of the following properties:overflow
(string): How to deal with out-of-range values.
Allowed values are 'constrain'
and 'reject'
.
The default is 'constrain'
.disambiguation
(string): How to handle date/time values that are ambiguous or invalid due to DST or other time zone offset changes.
Allowed values are 'compatible'
, 'earlier'
, 'later'
, and 'reject'
.
The default is 'compatible'
.offset
(string): How to handle conflicts between time zone offset and time zone.
Allowed values are 'prefer'
, 'use'
, 'ignore'
, and 'reject'
.
The default is 'prefer'
.Returns: a new Temporal.ZonedDateTime
object.
This method creates a new Temporal.ZonedDateTime
which is a copy of zonedDateTime
, but any properties present on zonedDateTimeLike
override the ones already present on zonedDateTime
.
Since Temporal.ZonedDateTime
objects each represent a fixed date and time, this method will create a new instance instead of modifying the existing instance.
If the result is earlier or later than the range of dates that Temporal.ZonedDateTime
can represent (approximately half a million years centered on the Unix epoch), then this method will throw a RangeError
regardless of overflow
.
NOTE: The allowed values for the
zonedDateTimeLike.month
property start at 1, which is different from legacyDate
where months are represented by zero-based indices (0 to 11).
If a timeZone
or calendar
property is included, this function will throw an exception.
To convert to a new time zone while updating the clock time, use the withTimeZone()
method, and to keep clock time as-is while resetting the time zone, use the .toPlainDateTime()
method instead. Examples:
// update local time to match new time zone
const sameInstantInOtherTz = zdt.withTimeZone('Europe/London');
// create instance with same local time in a new time zone
const newTzSameLocalTime = zdt.toPlainDateTime().toZonedDateTime('Europe/London');
Some input values can cause conflict between zonedDateTime
's time zone and its UTC offset.
This can happen when offset
is included in the input, and can also happen when setting date/time values on the opposite side of a time zone offset transition like DST starting or ending.
The offset
option can resolve this conflict.
Unlike the from()
method where offset
defaults to 'reject'
, the offset option in with
defaults to 'prefer'
.
This default prevents DST disambiguation from causing unexpected one-hour changes in exact time after making small changes to clock time fields.
For example, if a Temporal.ZonedDateTime
is set to the "second" 1:30AM on a day where the 1-2AM clock hour is repeated after a backwards DST transition, then calling .with({minute: 45})
will result in an ambiguity which is resolved using the default offset: 'prefer'
option.
Because the existing offset is valid for the new time, it will be retained so the result will be the "second" 1:45AM.
However, if the existing offset is not valid for the new result (e.g. .with({hour: 0})
), then the default behavior will change the offset to match the new local time in that time zone.
If the offset
option is set to 'ignore'
(or in very rare cases when 'prefer'
is used), then the object's current time zone and the disambiguation
option determine the offset is used for times that are ambiguous due to DST and other time zone offset transitions.
Otherwise, the offset
option determines the offset during skipped or repeated clock times and the disambiguation
option is ignored.
Other than the offset
option behaviors noted above, options on with
behave identically to options on from
.
See the documentation of from
for more details on options behavior.
Usage example:
zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24:00-06:00[America/Chicago]');
zdt.with({ year: 2015, minute: 31 }); // => 2015-12-07T03:31:00-06:00[America/Chicago]
Parameters:
plainTime
(optional Temporal.PlainTime
or plain object or string): The clock time that should replace the current clock time of zonedDateTime
.Returns: a new Temporal.ZonedDateTime
object which replaces the clock time of zonedDateTime
with the clock time represented by plainTime
.
The default plainTime
, if it's not provided, is the first valid local time in zonedDateTime
's time zone on its calendar date.
Usually this is midnight (00:00
), but may be a different time in rare circumstances like DST skipping midnight.
If provided, valid input to withPlainTime
is the same as valid input to Temporal.PlainTime.from
, including strings like 12:15:36
, plain object property bags like { hour: 20, minute: 30 }
, or Temporal
objects that contain time fields: Temporal.PlainTime
, Temporal.ZonedDateTime
, or Temporal.PlainDateTime
.
This method is similar to with
, but with a few important differences:
withPlainTime
accepts strings, Temporal objects, or object property bags.
with
only accepts object property bags and does not accept strings nor Temporal.PlainTime
objects because they can contain calendar information.withPlainTime
will default all missing time units to zero, while with
will only change units that are present in the input object.withPlainTime
does not accept options like disambiguation
or offset
.
For fine-grained control, use with
.If plainTime
is a Temporal.PlainTime
object, then this method returns the same result as plainTime.toZonedDateTime({ plainTime: zonedDateTime, timeZone: zonedDateTime})
but can be easier to use, especially when chained to previous operations that return a Temporal.ZonedDateTime
.
Usage example:
zdt = Temporal.ZonedDateTime.from('2015-12-07T03:24:30.000003500-08:00[America/Los_Angeles]');
zdt.withPlainTime({ hour: 10 }); // => 2015-12-07T10:00:00-08:00[America/Los_Angeles]
time = Temporal.PlainTime.from('11:22');
zdt.withPlainTime(time); // => 2015-12-07T11:22:00-08:00[America/Los_Angeles]
zdt.withPlainTime('12:34'); // => 2015-12-07T12:34:00-08:00[America/Los_Angeles]
// easier for chaining
zdt.add({ days: 2, hours: 22 }).withPlainTime('00:00'); // => 2015-12-10T00:00:00-08:00[America/Los_Angeles]
Parameters:
timeZone
(object or string): The time zone into which to project zonedDateTime
.Returns: a new Temporal.ZonedDateTime
object which is the date indicated by zonedDateTime
, projected into timeZone
.
Usage example:
zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24:30+09:00[Asia/Tokyo]');
zdt.toString(); // => '1995-12-07T03:24:30+09:00[Asia/Tokyo]'
zdt.withTimeZone('Africa/Accra').toString(); // => '1995-12-06T18:24:30+00:00[Africa/Accra]'
Parameters:
calendar
(object or string): The calendar into which to project zonedDateTime
.Returns: a new Temporal.ZonedDateTime
object which is the date indicated by zonedDateTime
, projected into calendar
.
Usage example:
zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24:30.000003500+09:00[Asia/Tokyo][u-ca=japanese]');
`${zdt.era} ${zdt.eraYear}`; // => 'heisei 7'
zdt.withCalendar('gregory').eraYear; // => 1995
Parameters:
duration
(object): A Temporal.Duration
object or a duration-like object.options
(optional object): An object which may have some or all of the following properties:overflow
(string): How to deal with additions that result in out-of-range values.
Allowed values are 'constrain'
and 'reject'
.
The default is 'constrain'
.Returns: a new Temporal.ZonedDateTime
object representing the sum of zonedDateTime
plus duration
.
This method adds duration
to zonedDateTime
.
The duration
argument is an object with properties denoting a duration, such as { hours: 5, minutes: 30 }
, or a Temporal.Duration
object. Adding a negative duration like { hours: -5, minutes: -30 }
is equivalent to subtracting the absolute value of that duration.
Addition and subtraction are performed according to rules defined in RFC 5545 (iCalendar):
Temporal.PlainDateTime
).
The result will automatically adjust for Daylight Saving Time using the rules of this instance's timeZone
field.Temporal.Instant
).disambiguation: 'compatible'
option used in from
and used by legacy Date
and moment.js.These rules make arithmetic with Temporal.ZonedDateTime
"DST-safe", which means that the results most closely match the expectations of both real-world users and implementers of other standards-compliant calendar applications. These expectations include:
Some arithmetic operations may be ambiguous, e.g. because months have different lengths.
For example, adding one month to August 31 would result in September 31, which doesn't exist.
For these cases, the overflow
option tells what to do:
'constrain'
mode (the default), out-of-range values are clamped to the nearest in-range value.'reject'
mode, a result that would be out of range causes a RangeError
to be thrown.Additionally, if the result is earlier or later than the range of dates that Temporal.ZonedDateTime
can represent (approximately half a million years centered on the Unix epoch), then this method will throw a RangeError
regardless of overflow
.
Usage example:
zdt = Temporal.ZonedDateTime.from('2020-03-08T00:00-08:00[America/Los_Angeles]');
// Add a day to get midnight on the day after DST starts
laterDay = zdt.add({ days: 1 });
// => 2020-03-09T00:00:00-07:00[America/Los_Angeles]
// Note that the new offset is different, indicating the result is adjusted for DST.
laterDay.since(zdt, { largestUnit: 'hour' }).hours;
// => 23
// because one clock hour lost to DST
laterHours = zdt.add({ hours: 24 });
// => 2020-03-09T01:00:00-07:00[America/Los_Angeles]
// Adding time units doesn't adjust for DST. Result is 1:00AM: 24 real-world
// hours later because a clock hour was skipped by DST.
laterHours.since(zdt, { largestUnit: 'hour' }).hours; // => 24
Parameters:
duration
(object): A Temporal.Duration
object or a duration-like object.options
(optional object): An object which may have some or all of the following properties:overflow
(string): How to deal with additions that result in out-of-range values.
Allowed values are 'constrain'
and 'reject'
.
The default is 'constrain'
.Returns: a new Temporal.ZonedDateTime
object representing the result of zonedDateTime
minus duration
.
This method subtracts a duration
from zonedDateTime
.
The duration
argument is an object with properties denoting a duration, such as { hours: 5, minutes: 30 }
, or a Temporal.Duration
object. Subtracting a negative duration like { hours: -5, minutes: -30 }
is equivalent to adding the absolute value of that duration.
Addition and subtraction are performed according to rules defined in RFC 5545 (iCalendar), as described above in add()
.
Some arithmetic operations may be ambiguous, e.g. because months have different lengths.
For example, subtracting one month from October 31 would result in September 31, which doesn't exist.
For these cases, the overflow
option tells what to do:
'constrain'
mode (the default), out-of-range values are clamped to the nearest in-range value.'reject'
mode, a result that would be out of range causes a RangeError
to be thrown.Additionally, if the result is earlier or later than the range of dates that Temporal.ZonedDateTime
can represent (approximately half a million years centered on the Unix epoch), then this method will throw a RangeError
regardless of overflow
.
Usage example:
zdt = Temporal.ZonedDateTime.from('2020-03-09T00:00-07:00[America/Los_Angeles]');
// Add a day to get midnight on the day after DST starts
earlierDay = zdt.subtract({ days: 1 });
// => 2020-03-08T00:00:00-08:00[America/Los_Angeles]
// Note that the new offset is different, indicating the result is adjusted for DST.
earlierDay.since(zdt, { largestUnit: 'hour' }).hours;
// => -23
// because one clock hour lost to DST
earlierHours = zdt.subtract({ hours: 24 });
// => 2020-03-07T23:00:00-08:00[America/Los_Angeles]
// Subtracting time units doesn't adjust for DST. Result is 11:00PM: 24 real-world
// hours earlier because a clock hour was skipped by DST.
earlierHours.since(zdt, { largestUnit: 'hour' }).hours; // => -24
Parameters:
other
(Temporal.LocalZonedDateTime
): Another date/time until when to compute the difference.options
(optional object): An object which may have some or all of the following properties:largestUnit
(string): The largest unit of time to allow in the resulting Temporal.Duration
object.
Valid values are 'auto'
, 'year'
, 'month'
, 'week'
, 'day'
, 'hour'
, 'minute'
, 'second'
, 'millisecond'
, 'microsecond'
, and 'nanosecond'
.
The default is 'auto'
.smallestUnit
(string): The smallest unit of time to round to in the resulting Temporal.Duration
object.
Valid values are 'year'
, 'month'
, 'week'
, 'day'
, 'hour'
, 'minute'
, 'second'
, 'millisecond'
, 'microsecond'
, and 'nanosecond'
.
The default is 'nanosecond'
, i.e., no rounding.roundingIncrement
(number): The granularity to round to, of the unit given by smallestUnit
.
The default is 1.roundingMode
(string): How to handle the remainder, if rounding.
Valid values are 'ceil'
, 'floor'
, 'expand'
, 'trunc'
, 'halfCeil'
, 'halfFloor'
, 'halfExpand'
, 'halfTrunc'
, and 'halfEven'
.
The default is 'trunc'
, which truncates any remainder towards zero.Returns: a Temporal.Duration
representing the elapsed time after zonedDateTime
and until other
.
This method computes the difference between the two times represented by zonedDateTime
and other
, optionally rounds it, and returns it as a Temporal.Duration
object.
If other
is earlier than zonedDateTime
then the resulting duration will be negative.
If using the default options
, adding the returned Temporal.Duration
to zonedDateTime
will yield other
.
The largestUnit
option controls how the resulting duration is expressed.
The returned Temporal.Duration
object will not have any nonzero fields that are larger than the unit in largestUnit
.
For example, a difference of two hours will become 7200 seconds when largestUnit
is "seconds"
.
However, a difference of 30 seconds will still be 30 seconds if largestUnit
is "hours"
.
A value of 'auto'
means 'hour'
, unless smallestUnit
is 'year'
, 'month'
, 'week'
, or 'day'
, in which case largestUnit
is equal to smallestUnit
.
You can round the result using the smallestUnit
, roundingIncrement
, and roundingMode
options.
These behave as in the Temporal.Duration.round()
method, but increments of days and larger are allowed.
Because rounding to an increment expressed in days or larger units requires a reference point, zonedDateTime
is used as the starting point in that case.
The default is to do no rounding.
The duration returned is a "hybrid" duration.
This means that the duration's date portion represents full calendar days like Temporal.PlainDateTime.prototype.until()
would return, while its time portion represents real-world elapsed time like Temporal.Instant.prototype.until()
would return.
This "hybrid duration" approach automatically adjusts for DST and matches widely-adopted industry standards like RFC 5545 (iCalendar).
It also matches the behavior of popular JavaScript libraries like moment.js and date-fns.
Examples:
P1DT1H
(even though it's only 24 hours of real-world elapsed time)PT24H30M
(because it hasn't been a full calendar day even though it's been 24.5 real-world hours).If largestUnit
is 'hour'
or smaller, then the result will be the same as if Temporal.Instant.prototype.until()
was used.
If both values have the same local time, then the result will be the same as if Temporal.PlainDateTime.prototype.until()
was used.
To calculate the difference between calendar dates only, use .toPlainDate().until(other.toPlainDate())
.
To calculate the difference between clock times only, use .toPlainTime().until(other.toPlainTime())
.
If the other Temporal.ZonedDateTime
is in a different time zone, then the same days can be different lengths in each time zone, e.g. if only one of them observes DST.
Therefore, a RangeError
will be thrown if largestUnit
is 'day'
or larger and the two instances' time zones are not equal, using the same equality algorithm as Temporal.ZonedDateTime.prototype.equals
.
To work around this same-time-zone requirement, transform one of the instances to the other's time zone using .withTimeZone(other.timeZone)
and then calculate the same-timezone difference.
Because of the complexity and ambiguity involved in cross-timezone calculations involving days or larger units, 'hour'
is the default for largestUnit
.
Take care when using milliseconds, microseconds, or nanoseconds as the largest unit.
For some durations, the resulting value may overflow Number.MAX_SAFE_INTEGER
and lose precision in its least significant digit(s).
Nanoseconds values will overflow and lose precision after about 104 days. Microseconds can fit about 285 years without losing precision, and milliseconds can handle about 285,000 years without losing precision.
Computing the difference between two dates in different calendar systems is not supported.
If you need to do this, choose the calendar in which the computation takes place by converting one of the dates with zonedDateTime.withCalendar
.
Usage example:
zdt1 = Temporal.ZonedDateTime.from('1995-12-07T03:24:30.000003500+05:30[Asia/Kolkata]');
zdt2 = Temporal.ZonedDateTime.from('2019-01-31T15:30+05:30[Asia/Kolkata]');
zdt1.until(zdt2);
// => PT202956H5M29.9999965S
zdt1.until(zdt2, { largestUnit: 'year' });
// => P23Y1M24DT12H5M29.9999965S
zdt2.until(zdt1, { largestUnit: 'year' });
// => -P23Y1M24DT12H5M29.9999965S
zdt1.until(zdt2, { largestUnit: 'nanosecond' });
// => PT730641929.999996544S
// (precision lost)
// Rounding, for example if you don't care about sub-seconds
zdt1.until(zdt2, { smallestUnit: 'second' });
// => PT202956H5M29S
// Months and years can be different lengths
[jan1, feb1, mar1] = [1, 2, 3].map((month) =>
Temporal.ZonedDateTime.from({ year: 2020, month, day: 1, timeZone: 'Asia/Seoul' })
);
jan1.until(feb1, { largestUnit: 'day' }); // => P31D
jan1.until(feb1, { largestUnit: 'month' }); // => P1M
feb1.until(mar1, { largestUnit: 'day' }); // => P29D
feb1.until(mar1, { largestUnit: 'month' }); // => P1M
jan1.until(mar1, { largestUnit: 'day' }); // => P60D
Parameters:
other
(Temporal.LocalZonedDateTime
): Another date/time since when to compute the difference.options
(optional object): An object which may have some or all of the following properties:largestUnit
(string): The largest unit of time to allow in the resulting Temporal.Duration
object.
Valid values are 'auto'
, 'year'
, 'month'
, 'week'
, 'day'
, 'hour'
, 'minute'
, 'second'
, 'millisecond'
, 'microsecond'
, and 'nanosecond'
.
The default is 'auto'
.smallestUnit
(string): The smallest unit of time to round to in the resulting Temporal.Duration
object.
Valid values are 'year'
, 'month'
, 'week'
, 'day'
, 'hour'
, 'minute'
, 'second'
, 'millisecond'
, 'microsecond'
, and 'nanosecond'
.
The default is 'nanosecond'
, i.e., no rounding.roundingIncrement
(number): The granularity to round to, of the unit given by smallestUnit
.
The default is 1.roundingMode
(string): How to handle the remainder, if rounding.
Valid values are 'ceil'
, 'floor'
, 'expand'
, 'trunc'
, 'halfCeil'
, 'halfFloor'
, 'halfExpand'
, 'halfTrunc'
, and 'halfEven'
.
The default is 'trunc'
, which truncates any remainder towards zero.Returns: a Temporal.Duration
representing the elapsed time before zonedDateTime
and since other
.
This method computes the difference between the two times represented by zonedDateTime
and other
, optionally rounds it, and returns it as a Temporal.Duration
object.
If other
is later than zonedDateTime
then the resulting duration will be negative.
This method is similar to Temporal.ZonedDateTime.prototype.until()
, but reversed.
If using the default options
, subtracting the returned Temporal.Duration
from zonedDateTime
will yield other
, and zdt1.since(zdt2)
will yield the same result as zdt1.until(zdt2).negated()
.
Usage example:
zdt1 = Temporal.ZonedDateTime.from('1995-12-07T03:24:30.000003500+05:30[Asia/Kolkata]');
zdt2 = Temporal.ZonedDateTime.from('2019-01-31T15:30+05:30[Asia/Kolkata]');
zdt2.since(zdt1); // => PT202956H5M29.9999965S
Parameters:
roundTo
(string | object): A required string or object to control the operation.Temporal.ZonedDateTime
object will be rounded to that unit.
Valid values are 'day'
, 'hour'
, 'minute'
, 'second'
, 'millisecond'
, 'microsecond'
, and 'nanosecond'
.
A string parameter is treated the same as an object whose smallestUnit
property value is that string.smallestUnit
(required string): The unit to round to.
Valid values are 'day'
, 'hour'
, 'minute'
, 'second'
, 'millisecond'
, 'microsecond'
, and 'nanosecond'
.roundingIncrement
(number): The granularity to round to, of the unit given by smallestUnit
.
The default is 1.roundingMode
(string): How to handle the remainder.
Valid values are 'ceil'
, 'floor'
, 'expand'
, 'trunc'
, 'halfCeil'
, 'halfFloor'
, 'halfExpand'
, 'halfTrunc'
, and 'halfEven'
.
The default is 'halfExpand'
.Returns: a new Temporal.ZonedDateTime
object which is zonedDateTime
rounded to roundTo
(if a string parameter is used) or roundingIncrement
of smallestUnit
(if an object parameter is used).
Rounds zonedDateTime
to the given unit and increment, and returns the result as a new Temporal.ZonedDateTime
object.
The smallestUnit
option (or the value of roundTo
if a string parameter is used) determines the unit to round to.
For example, to round to the nearest minute, use smallestUnit: 'minute'
.
This option is required.
The roundingIncrement
option allows rounding to an integer number of units.
For example, to round to increments of a half hour, use { smallestUnit: 'minute', roundingIncrement: 30 }
.
The value given as roundingIncrement
must divide evenly into the next highest unit after smallestUnit
, and must not be equal to it.
(For example, if smallestUnit
is 'minute'
, then the number of minutes given by roundingIncrement
must divide evenly into 60 minutes, which is one hour.
The valid values in this case are 1 (default), 2, 3, 4, 5, 6, 10, 12, 15, 20, and 30.
Instead of 60 minutes, use 1 hour.)
If smallestUnit
is 'day'
, then 1 is the only allowed value for roundingIncrement
.
The roundingMode
option controls how the rounding is performed.
'ceil'
, 'expand'
: Always round up, towards the end of time.'floor'
, 'trunc'
: Always round down, towards the beginning of time.'halfCeil'
, 'halfExpand'
: Round to the nearest of the values allowed by roundingIncrement
and smallestUnit
.
When there is a tie, round up, like 'ceil'
.'halfFloor'
, 'halfTrunc'
: Round to the nearest of the allowed values, like 'halfExpand'
, but when there is a tie, round down, like 'floor'
.'halfEven'
: Round to the nearest of the allowed values, but when there is a tie, round towards the value that is an even multiple of roundingIncrement
.
For example, with a roundingIncrement
of 2, the number 7 would round up to 8 instead of down to 6, because 8 is an even multiple of 2 (2 × 4 = 8, and 4 is even), whereas 6 is an odd multiple (2 × 3 = 6, and 3 is odd).Several pairs of modes behave the same as each other, but are both included for consistency with Temporal.Duration.round()
, where they are not the same.
The default rounding mode is 'halfExpand'
to match how rounding is often taught in school.
Note that this is different than the 'trunc'
default used by until
and since
options because rounding up would be an unexpected default for those operations.
Other properties behave identically between these methods.
Example usage:
zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24:30.000003500-08:00[America/Los_Angeles]');
// Round to a particular unit
zdt.round({ smallestUnit: 'hour' });
// => 1995-12-07T03:00:00-08:00[America/Los_Angeles]
// Round to an increment of a unit, e.g. half an hour:
zdt.round({ roundingIncrement: 30, smallestUnit: 'minute' });
// => 1995-12-07T03:30:00-08:00[America/Los_Angeles]
// Round to the same increment but round down instead:
zdt.round({ roundingIncrement: 30, smallestUnit: 'minute', roundingMode: 'floor' });
// => 1995-12-07T03:00:00-08:00[America/Los_Angeles]
Returns: A new Temporal.ZonedDateTime
instance representing the earliest valid local clock time during the current calendar day and time zone of zonedDateTime
.
This method returns a new Temporal.ZonedDateTime
indicating the start of the day.
The local time of the result is almost always 00:00
, but in rare cases it could be a later time e.g. if DST skips midnight in a time zone. For example:
const zdt = Temporal.ZonedDateTime.from('2015-10-18T12:00-02:00[America/Sao_Paulo]');
zdt.startOfDay(); // => 2015-10-18T01:00:00-02:00[America/Sao_Paulo]
When the same day starts twice (due to an offset transition), the earlier time is used for startOfDay
. For example, the America/St_Johns
time zone transitioned from offset -02:30
to offset -03:30
on 2010-11-07:
const zdt = Temporal.ZonedDateTime.from('2010-11-07T23:00:00-03:30[America/St_Johns]');
zdt.startOfDay(); // 2010-11-07T00:00:00-02:30[America/St_Johns]
Usage example:
zdt = Temporal.ZonedDateTime.from('2020-01-01T12:00-08:00[America/Los_Angeles]').startOfDay();
// => 2020-01-01T00:00:00-08:00[America/Los_Angeles]
zdt = Temporal.ZonedDateTime.from('2018-11-04T12:00-02:00[America/Sao_Paulo]').startOfDay();
// => 2018-11-04T01:00:00-02:00[America/Sao_Paulo]
// Note the 1:00AM start time because the first clock hour was skipped due to DST transition
// that started at midnight.
Parameters:
direction
(string | object): A required string or object to control the operation.
A string parameter is treated the same as an object whose direction
property value is that string.
If an object is passed, the following properties are recognized:direction
(required string): The direction in which to search for the closest UTC offset transition.
Valid values are 'next'
and 'previous'
.Returns: A Temporal.ZonedDateTime
object representing the following UTC offset transition in zonedDateTime
's time zone in the given direction, or null
if no transitions farther than zonedDateTime
could be found.
This method is used to calculate the closest past or future UTC offset transition from zonedDateTime
for its time zone.
A "transition" is a point in time where the UTC offset of a time zone changes, for example when Daylight Saving Time starts or stops.
Transitions can also be caused by other political changes like a country permanently changing the UTC offset of its time zone.
The returned Temporal.ZonedDateTime
will represent the first nanosecond where the newer UTC offset is used, not the last nanosecond where the previous UTC offset is used.
When no more transitions are expected in the given directoin, this method will return null
.
Some time zones (e.g., Etc/GMT+5
or -05:00
) have no offset transitions.
If zonedDateTime
has one of these time zones, this method will always return null
.
Example usage:
// How long until the next offset change from now, in the current location?
tz = Temporal.Now.timeZoneId();
now = Temporal.Now.zonedDateTimeISO(tz);
nextTransition = now.getTimeZoneTransition('next');
duration = nextTransition.since(now);
duration.toLocaleString(); // output will vary
// How long until the previous offset change from now, in the current location?
previousTransition = now.getTimeZoneTransition('previous');
duration = now.since(previousTransition);
duration.toLocaleString(); // output will vary
Parameters:
other
(Temporal.ZonedDateTime
): Another date/time to compare.Returns: true
if zonedDateTime
and other
are have equivalent fields (date/time fields, offset, time zone ID, and calendar ID), or false
if not.
Compares two Temporal.ZonedDateTime
objects for equality.
This function exists because it's not possible to compare using zonedDateTime == other
or zonedDateTime === other
, due to ambiguity in the primitive representation and between Temporal types.
If you don't need to know the order in which two events occur, then this function is easier to use than Temporal.ZonedDateTime.compare
.
However, there are subtle differences between the two methods—a true
result from equals
includes comparison of calendar and time zone, and is therefore stronger than a 0
result from compare (which ignores calendar and time zone).
Note that two Temporal.ZonedDateTime
instances can have the same clock time, time zone, and calendar but still be unequal, e.g. when a clock hour is repeated after DST ends in the Fall.
In this case, the two instances will have different offsetNanoseconds
field values.
To ignore calendars, convert both instances to use the ISO 8601 calendar:
zdt.withCalendar('iso8601').equals(other.withCalendar('iso8601'));
To ignore both time zones and calendars, compare the instants of both:
zdt.toInstant().equals(other.toInstant());
To compare time zone IDs directly, compare two ZonedDateTimes with the same instant and calendar:
zdt.withTimeZone(id1).equals(zdt.withTimeZone(id2));
The time zones of zonedDateTime and other are considered equivalent by the following algorithm:
Note that "resolve to the same Zone name" noted above is behavior that can vary between ECMAScript and other consumers of the IANA Time Zone Database. ECMAScript implementations generally do not allow identifiers to be equivalent if they represent different ISO 3166-1 Alpha-2 country codes. However, non-ECMAScript platforms may merge Zone names across country boundaries. See above to learn more about this variation.
Time zones that resolve to different Zones in the IANA Time Zone Database are not equivalent, even if those Zones always use the same offsets. Offset time zones and IANA time zones are also never equivalent.
Example usage:
zdt1 = Temporal.ZonedDateTime.from('1995-12-07T03:24:30.000003500+01:00[Europe/Paris]');
zdt2 = Temporal.ZonedDateTime.from('1995-12-07T03:24:30.000003500+01:00[Europe/Brussels]');
zdt1.equals(zdt2); // => false (same offset but different time zones)
zdt1.equals(zdt1); // => true
// To compare time zone IDs, use withTimeZone() with each ID on the same
// ZonedDateTime instance, and use equals() to compare
kolkata = zdt1.withTimeZone('Asia/Kolkata');
kolkata.equals(zdt.withTimeZone('Asia/Calcutta')); // => true
// Offset time zones are never equivalent to named time zones
kolkata.equals(zdt.withTimeZone('+05:30')); // => false
zeroOffset = zdt1.withTimeZone('+00:00');
zeroOffset.equals(zdt1.withTimeZone('UTC')); // => false
// For offset time zones, any valid format is accepted
zeroOffset.equals(zdt1.withTimeZone('+00:00')); // => true
zeroOffset.equals(zdt1.withTimeZone('+0000')); // => true
zeroOffset.equals(zdt1.withTimeZone('+00')); // => true
Parameters:
options
(optional object): An object with properties influencing the formatting.
The following options are recognized:offset
(string): Whether to show the time zone offset in the return value.
Valid values are 'auto'
and 'never'
.
The default is 'auto'
.timeZoneName
(string): Whether to show the time zone name annotation in the return value.
Valid values are 'auto'
, 'never'
, and 'critical'
.
The default is 'auto'
.calendarName
(string): Whether to show the calendar annotation in the return value.
Valid values are 'auto'
, 'always'
, 'never'
, and 'critical'
.
The default is 'auto'
.fractionalSecondDigits
(number or string): How many digits to print after the decimal point in the output string.
Valid values are 'auto'
, 0, 1, 2, 3, 4, 5, 6, 7, 8, or 9.
The default is 'auto'
.smallestUnit
(string): The smallest unit of time to include in the output string.
This option overrides fractionalSecondDigits
if both are given.
Valid values are 'minute'
, 'second'
, 'millisecond'
, 'microsecond'
, and 'nanosecond'
.roundingMode
(string): How to handle the remainder.
Valid values are 'ceil'
, 'floor'
, 'expand'
, 'trunc'
, 'halfCeil'
, 'halfFloor'
, 'halfExpand'
, 'halfTrunc'
, and 'halfEven'
.
The default is 'trunc'
.Returns: a string containing an ISO 8601 date+time+offset format, a bracketed time zone suffix, and (if the calendar is not iso8601
) a calendar suffix.
Examples:
2011-12-03T10:15:30+01:00[Europe/Paris]
2011-12-03T10:15:30+09:00[Asia/Tokyo][u-ca=japanese]
This method overrides the Object.prototype.toString()
method and provides a convenient string representation of zonedDateTime
.
The string is "round-trippable".
This means that it can be passed to Temporal.ZonedDateTime.from()
to create a new Temporal.ZonedDateTime
object with the same field values as the original.
The output precision can be controlled with the fractionalSecondDigits
or smallestUnit
option.
If no options are given, the default is fractionalSecondDigits: 'auto'
, which omits trailing zeroes after the decimal point.
The value is truncated to fit the requested precision, unless a different rounding mode is given with the roundingMode
option, as in Temporal.PlainDateTime.round()
.
Note that rounding may change the value of other units as well.
Normally, a calendar annotation is shown when zonedDateTime
's calendar is not the ISO 8601 calendar.
By setting the calendarName
option to 'always'
or 'never'
this can be overridden to always or never show the annotation, respectively.
Normally not necessary, a value of 'critical'
is equivalent to 'always'
but the annotation will contain an additional !
for certain interoperation use cases.
For more information on the calendar annotation, see ISO string extensions.
Likewise, passing 'never'
to the timeZoneName
or offset
options controls whether the time zone offset (+01:00
) or name annotation ([Europe/Paris]
) are shown.
If the time zone offset is shown, it is always shown rounded to the nearest minute.
The timeZoneName
option can additionally be 'critical'
which will add an additional !
to the annotation, similar to calendarName
.
The string format output by this method can be parsed by java.time.ZonedDateTime
as long as the calendar annotation is not output and 'critical'
is not used.
For more information on Temporal
's extensions to the ISO 8601 / RFC 3339 string format and the progress towards becoming a published standard, see String Parsing, Serialization, and Formatting.
Example usage:
zdt = Temporal.ZonedDateTime.from({ year: 2019, month: 12, day: 1, hour: 12, timeZone: 'Africa/Lagos' });
zdt.toString(); // => '2019-12-01T12:00:00+01:00[Africa/Lagos]'
zdt = zdt.withCalendar('japanese');
zdt.toString(); // => '2019-12-01T12:00:00+01:00[Africa/Lagos][u-ca=japanese]'
Parameters:
locales
(optional string or array of strings): A string with a BCP 47 language tag with an optional Unicode extension key, or an array of such strings.options
(optional object): An object with properties influencing the formatting.Returns: a language-sensitive representation of zonedDateTime
.
This method overrides Object.prototype.toLocaleString()
to provide a human-readable, language-sensitive representation of zonedDateTime
.
The locales
and options
arguments are the same as in the constructor to Intl.DateTimeFormat
.
options.timeZone
will be automatically set from the time zone of zonedDateTime
.
If a different time zone ID is provided in options.timeZone
, a RangeError will be thrown.
To display a Temporal.ZonedDateTime
value in a different time zone, use withTimeZone(timeZone).toLocaleString()
.
Example usage:
zdt = Temporal.ZonedDateTime.from('2019-12-01T12:00+01:00[Europe/Berlin]');
zdt.toLocaleString(); // example output: 12/1/2019, 12:00:00 PM
zdt.toLocaleString('de-DE'); // => '1.12.2019, 12:00:00 MEZ'
options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
zdt.toLocaleString('de-DE', options); // => 'Sonntag, 1. Dezember 2019'
/* WRONG */ zdt.toLocaleString('de-DE', { timeZone: 'Pacific/Auckland' });
// => RangeError: Time zone option Pacific/Auckland does not match actual time zone Europe/Berlin
zdt.withTimeZone('Pacific/Auckland').toLocaleString('de-DE'); // => '2.12.2019, 0:00:00 GMT+13'
zdt.toLocaleString('en-US-u-nu-fullwide-hc-h12'); // => '12/1/2019, 12:00:00 PM GMT+1'
Returns: a string in the ISO 8601 date format representing zonedDateTime
.
This method is the same as zonedDateTime.toString()
.
It is usually not called directly, but it can be called automatically by JSON.stringify()
.
The reverse operation, recovering a Temporal.ZonedDateTime
object from a string, is Temporal.ZonedDateTime.from()
, but it cannot be called automatically by JSON.parse()
.
If you need to rebuild a Temporal.ZonedDateTime
object from a JSON string, then you need to know the names of the keys that should be interpreted as Temporal.ZonedDateTime
s.
In that case you can build a custom "reviver" function for your use case.
Example usage:
const event = {
id: 311,
name: 'FictionalConf 2018',
openingZonedDateTime: Temporal.ZonedDateTime.from('2018-07-06T10:00+05:30[Asia/Kolkata]'),
closingZonedDateTime: Temporal.ZonedDateTime.from('2018-07-08T18:15+05:30[Asia/Kolkata]')
};
const str = JSON.stringify(event, null, 2);
console.log(str);
// =>
// {
// "id": 311,
// "name": "FictionalConf 2018",
// "openingZonedDateTime": "2018-07-06T10:00+05:30[Asia/Kolkata]",
// "closingZonedDateTime": "2018-07-08T18:15+05:30[Asia/Kolkata]"
// }
// To rebuild from the string:
function reviver(key, value) {
if (key.endsWith('ZonedDateTime')) return Temporal.ZonedDateTime.from(value);
return value;
}
JSON.parse(str, reviver);
This method overrides Object.prototype.valueOf()
and always throws an exception.
This is because it's not possible to compare Temporal.ZonedDateTime
objects with the relational operators <
, <=
, >
, or >=
.
Use Temporal.ZonedDateTime.compare()
for this, or zonedDateTime.equals()
for equality.
Returns: A Temporal.Instant
object that represents the same instant as zonedDateTime
.
Returns: a Temporal.PlainDate
object that is the same as the date portion of zonedDateTime
.
Returns: a Temporal.PlainTime
object that is the same as the wall-clock time portion of zonedDateTime
.
Returns: a Temporal.PlainDateTime
object that is the same as the date and time portion of zonedDateTime
.
NOTE: After a
Temporal.ZonedDateTime
is converted toTemporal.PlainDateTime
, it will no longer be aware of its time zone. This means that subsequent operations like arithmetic orwith
will not adjust for DST and may not yield the same results as equivalent operations withTemporal.ZonedDateTime
. However, unless you perform those operations across a time zone offset transition, it's impossible to notice the difference. Therefore, be very careful when performing this conversion because subsequent results may look correct most of the time while failing around time zone transitions like when DST starts or ends.
The above four methods can be used to convert Temporal.ZonedDateTime
into a Temporal.Instant
, Temporal.PlainDate
, Temporal.PlainTime
, or Temporal.PlainDateTime
, respectively.
The converted object carries a copy of all the relevant data of zonedDateTime
(for example, in toPlainDate()
, the year
, month
, and day
properties are the same.)
To convert to Temporal.PlainYearMonth
or Temporal.PlainMonthDay
, first use toPlainDate()
and go from there.
Usage example:
zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24:30+02:00[Africa/Johannesburg]');
zdt.toInstant(); // => 1995-12-07T01:24:30Z
zdt.toPlainDateTime(); // => 1995-12-07T03:24:30
zdt.toPlainDate(); // => 1995-12-07
zdt.toPlainTime(); // => 03:24:30
zdt.toPlainDate().toPlainYearMonth(); // => 1995-12
zdt.toPlainDate().toPlainMonthDay(); // => 12-07