Temporal is the proposal of a new date & time handling API for ECMA-Script. The design principles are:
- All temporal APIs are non-mutating. All temporal objects are effectively immutable.
- All date values are based on the Proleptic Gregorian Calendar. Other calendar systems are out-of-scope for this proposal. However, we will consider how future APIs may interact with this one such that extending it to support other calendars may be possible in a future proposal.
- All time-of-day values are based on a standard 24-hour clock.
- Leap seconds are not represented.
There is a subset of temporal objects that are tied to the absolute timeline meaning they are relative to a specific and specified point in time. In this case that time is the POSIX-epoch and follows the rules established in ECMA-262. They represent a specific point in time.
These are Instant
, OffsetDateTime
, and ZonedDateTime
in order of amount of detail information available in them.
Instant
just represents an absolute point in time. No timezone or offset information is present. As such Instants have no concept of days or months or even hours.
OffsetDateTime
is the combination of an Instant with an offset from UTC. As such it has the ability to know about days, months, etc. However it does not know what timezone or locality it is in. As such it cannot know which daylight saving rules apply.
ZonedDateTime
represents an OffsetDateTime
combined with an IANA Timezone. With that added information ZonedDateTime is able to observe daylight saving rules.
All temporal object are immutable in that their properties are getters only.
An Instant
is an object that specifies a specific point in time. For convenience of interoperability it uses nanoseconds since the unix-epoch to do so.
The epochSeconds
property of an Instant
object represents the seconds since POSIX-epoch.
The epochMilliseconds
property of an Instant
object represents the whole milliseconds since POSIX-epoch.
The epochMicroseconds
property of an Instant
object represents the whole microseconds since POSIX-epoch.
The epochNanoseconds
property of an Instant
object represents the nanoseconds since POSIX-epoch.
Instant.prototype.withZone(zone: string) : ZonedDateTime
This creates a ZonedDateTime
by applying a IANA timezone to the instant.
This is equivalent to new ZonedDateTime(instant, zone)
Instant.prototype.withOffset(offset: string) : OffsetDateTime
This creates a OffsetDateTime
by applying an offset-string to the instant.
This is equivalent to new OffsetDateTime(instant, offset)
Instant.fromString(iso: string) : Instant
Parses a string
that must be in the same ISO-8601 format as produced by Instant.prototype.toString()
and creates a new Instant
object from it.
Instant.fromEpochNanoseconds(epochnanos: BigInt) : Instant
Equivalent to new Instant(epochnanos)
.
Instant.fromEpochMicroseconds(epochmicros: BigInt) : Instant
Equivalent to Instant.fromEpochNanoseconds(epochmicros * 1000n)
.
Instant.fromEpochMilliseconds(epochmillis: number) : Instant
Equivalent to Instant.fromEpochMicroseconds(BigInt(epochmillis) * 1000n)
.
Instant.fromEpochSeconds(epochseconds: number) : Instant
Equivalent to Instant.fromEpochMilliseconds(seconds * 1000)
.
An OffsetDateTime
is an object that specifies a specific point in time with a specific offset from UTC.
It bases this on an Instant
and an offset string
.
The constructor may only be called as such. It takes two arguments. The first is an instance
of Instant
and the second is a string
representing a valid time offset.
The instance of Instant
that is being referenced by this OffsetDateTime
.
The time-zone offset represented as a string
.
This must always have the format: sign``hours
:minutes
where
sign
is either+
or-
hours
is the hours offset 0-padded to 2 digits.minutes
is the minutes offset 0-padded to 2 digits.
Examples: +00:00
, -04:00
, +03:00
, ...
The .year
property represents the year of the OffsetDateTime
according to the proleptic Gregorian calendar with a midnight to midnight 24 hour day.
The .month
property represents the month of the OffsetDateTime
according to the proleptic Gregorian calendar with a midnight to midnight 24 hour day. This value is guaranteed to be between 1
and 12
inclusive.
The .day
property represents the day of the OffsetDateTime
according to the proleptic Gregorian calendar with a midnight to midnight 24 hour day.
The .hour
property represents the hour of the OffsetDateTime
from 0
to 23
inclusive according to the proleptic Gregorian calendar with a midnight to midnight 24 hour day.
The .minute
property represents the minute of the hour of the OffsetDateTime
from 0
to 59
inclusive according to the proleptic Gregorian calendar with a midnight to midnight 24 hour day.
The .second
property represents the whole seconds of the minute of the OffsetDateTime
from 0
to 59
inclusive according to the proleptic Gregorian calendar with a midnight to midnight 24 hour day.
The .millisecond
property represents the whole milliseconds of the seconds of the OffsetDateTime
from 0
to 999
inclusive according to the proleptic Gregorian calendar with a midnight to midnight 24 hour day.
The .microsecond
property represents the whole microseconds of the milliseconds of the OffsetDateTime
from 0
to 999
inclusive according to the proleptic Gregorian calendar with a midnight to midnight 24 hour day.
The .nanosecond
property represents the nanoseconds of the microseconds of the OffsetDateTime
from 0
to 999
inclusive according to the proleptic Gregorian calendar with a midnight to midnight 24 hour day.
The .dayOfWeek
property represents the day of the week according to the proleptic Gregorian calendar with a midnight to midnight 24 hour day where Monday is 1
and Sunday
is 7
in accordance with ISO-8601.
The .dayOfYear
property represents the ordinal day of the Gregorian year from 1
to 366
inclusive according to ISO-8601.
The .weekOfYear
property represents the ISO week-number from 1
to 53
inclusive. Beware that dates at the begining of a year may be part of a week from the preceding year, and dates at the end of a year may be part of a week at the beginning of the next year, as the first week of any year is defined as the week that contains the first Thursday of the week.
OffsetDateTime.prototype.with(data: DateTimeLike): OffsetDateTime
OffsetDateTime.prototype.plus(data: DurationLike): OffsetDateTime
Creates a new OffsetDateTime
object by adding values to its members. The specified values
must be numeric if specified and will be cast to a scaled integer (by calulating the absolute
value and flooring it).
The algorithm is such that:
- the individual values are added to the existing values.
- the range of
nanoseconds
is ensured to be between 0 and 999 by adjusting themicrosecond
- the range of
microseconds
is ensured to be between 0 and 999 by adjusting themillisecond
- the range of
milliseconds
is ensured to be between 0 and 999 by adjusting thesecond
- the range of
seconds
is ensured to be between 0 and 59 by adjusting theminute
- the range of
minutes
is ensured to be between 0 and 59 by adjusting thehour
- the range of
hours
is ensured to be between 0 and 23 by adjustingday
- the range of
days
is ensured to be between 1 and 29-31 depending on the month by adjustingmonth
- the range of
months
is ensured to be between 1 and 12 by adjusting theyear
.
OffsetDateTime.prototype.minus(other: ZonedDateTime): Duration
Creates a new OffsetDateTime
object by subtracting values to its members. The specified values
must be numeric if specified and will be cast to a scaled integer (by calulating the absolute
value and flooring it).
OffsetDateTime.prototype.withZone(iana: string) : ZonedDateTime
Creates a new ZonedDateTime
object representing the same point in time with the passed IANA-Timezone. If the IANA-Timezone is invalid this method throws.
OffsetDateTime.prototype.getCivilDateTime() : CivilDateTime
OffsetDateTime.prototype.getCivilDate() : CivilDate
OffsetDateTime.prototype.getCivilTime() : CivilTime
OffsetDateTime.prototype.getCivilYearMonth() : CivilYearMonth
OffsetDateTime.prototype.getCivilMonthDay() : CivilMonthDay
This creates an ISO-8601 string in the following format:
${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${nanoseconds}${offset}
The year
is 0-padded to a minimum of 4 digits. month
, day
, hours
, minutes
, seconds
are 0-padded to a minimum of 2 digits. nanoseconds
is 0-padded to a minimum of 9 digits. The offset
is the timezone offset as created by zoned.offset
.
Examples:
1976-11-18T15:23:30.123456789+01:00
- created with+01:00
offset
Equivalent to OffsetDateTime.prototype.toString() : string
OffsetDateTime.fromString(iso: string) : ZonedDateTime
Creates a new OffsetDateTime
object from parsing a string that must be in the same ISO 8601 format used for OffsetDateTime.prototype.toString()
, with precision at or exceeding the minute level and non-empty non-"Z" UTC offset representation.
A ZonedDateTime
is an object that specifies a specific point in time with an IANA timezone.
It bases this on an Instant
and an IANA string
.
ZonedDateTime(instant: Instant, zone: string) - constructor
The constructor may only be called as such. It takes two arguments. The first is an instance
of Instant
and the second is a string
representing a valid IANA timezone.
ZonedDateTime.prototype
includes all properties and methods of OffsetDateTime.prototype
except plus
& minus
.
In addition the following properties & methods are defined:
The IANA-Timezone used by this ZonedDateTime
.
ZonedDateTime.prototype.getOffsetDateTime() : OffsetDateTime
This creates an ISO-8601 string in the following format
${year}-${month}-${day}T${hours}:${minutes}:${seconds}.${nanoseconds}${offset}[${iana}]
The year
is 0-padded to a minimum of 4 digits. month
, day
, hours
, minutes
, seconds
are 0-padded to a minimum of 2 digits. nanoseconds
is 0-padded to a minimum of 9 digits. The
offset
is the timezone offset as created by zoned.offset.
iana` is the IANA-Timezone
string.
Examples:
1976-11-18T15:23:30.123456789+01:00[Europe/Vienna]
- created withEurope/Vienna
timezone1976-11-18T15:23:30.123456789+01:00[Europe/Berlin]
- created withEurope/Berlin
timezone
ZonedDateTime.fromString(iso: string) : ZonedDateTime
Creates a new ZonedDateTime
object from parsing a string that must be in the same ISO 8601 format used for ZonedDateTime.prototype.toString()
, with precision at or exceeding the minute level, non-empty non-"Z" UTC offset representation, and a bracketed IANA time zone name in which the offset is correct for the represented date and time.
A method that can be used to check if a give string is a valid IANA-Timezone representation.
Unbound objects are objects that do not represent a date or time tied to the actual concrete timeline. Imagine a note saying "be home by 14:45". That information is not tied to a specific point in time. You don't know where the note came from. It could have been written at any time in the past. No date attaches to it. Equally you can't tell what timezone it belongs in. It may not mean local time.
Even information like "I was born on the 18th of November 1976 at 15:23:30" does not attach to a specific point in time. We still lack the location of where the speaker was born.
In addition there are more abstract dates that are used frequently in civil discourse. Such as "Christmas is on the 25th of December". Which year? Or "I moved to Britain in August 2014". Which day in August?
These objects all have in common is that they are not tied to a specific point on the timeline.
Again all temporal object are immutable in that their properties are getters only.
CivilDate
(and its siblings) represents a date and time corresponding to the requirements of ISO-8601.
This means specifically that it uses the proleptic Gregorian calendar whose days are 24 hours long and begin and end at midnight.
The constructor may only be called as such. It takes 3 numeric arguments.
year
the Gregorian yearmonth
the Gregorian monthday
the Gregorian day of the month
The .year
property represents the year of the CivilDate
.
The .month
property represents the month of the CivilDate
.
The .day
property represents the day of the month of the CivilDate
.
The .dayOfWeek
property represents the day of the week where Monday is 1
and Sunday
is 7
in accordance with ISO-8601.
The .dayOfYear
property represents the ordinal day of the Gregorian year according to ISO-8601.
The .weekOfYear
property represents the ISO week-number. Beware that dates at the begining of a year may be part of a week from the preceding year, and dates at the end of a year may be part of a week at the beginning of the next year, as the first week of any year is defined as the week that contains the first Thursday of the week.
CivilDate.prototype.plus(value: DurationLike) : CivilDate
Creates a new CivilDate
object by adding values to its members.
The specified values must be numeric if specified. The specified values
must be numeric if specified and will be cast to a scaled integer (by calulating the absolute
value and flooring it).
The algorithm is such that:
- the individual values are added to the existing values.
- the range of
days
is ensured to be between 1 and 29-31 depending on the month by adjustingmonth
- the range of
months
is ensured to be between 1 and 12 by adjusting theyears
.
CivilDate.prototype.plus(value: DurationLike) : CivilDate
Creates a new CivilDate
object by subtracting values to its members.
The specified values must be numeric if specified. The specified values
must be numeric if specified and will be cast to a scaled integer (by calulating the absolute
value and flooring it).
Creates a new CivilDate
object by overriding specified values to its members.
The specified values must be numeric if specified.
CivilDate.prototype.plus(values: DurationLike) : CivilDate
CivilDate.prototype.minus(values: DurationLike) : CivilDate
CivilDate.prototype.withTime(time : CivilTime) : CivilDateTime
Combines this CivilDate
with the passed CivilTime
to create a new CivilDateTime
object.
Equivalent to date.toDateString()
Equivalent to date.toString()
CivilDate.fromString(isostring: string): CivilDate
Creates a new CivilDate
by parsing an ISO-8601 string in the format created by .toString()
.
CivilTime
(and its siblings) represents a time corresponding to the requirements of ISO-8601.
This means specifically that days are 24 hours long and begin and end at midnight.
The constructor may only be called as such. It takes between 2 and 4 numeric arguments.
hours
hour of the dayminutes
minutes of the hourseconds
seconds of the minutes (default: 0)milliseconds
milliseconds of the second (default: 0)microseconds
microseconds of the millisecond (default: 0)nanoseconds
nanoseconds of the microseconds (default: 0)
The .hour
property represents the hour of the CivilTime
.
The .minute
property represents the minute of the hour of the CivilTime
.
The .second
property represents the second of the minute of the CivilTime
.
The .millisecond
property represents the sub-second component of the second of the CivilTime
with millisecond precision. It will have a value between 0 and 999.
The .microsecond
property represents the sub-millisecond component of the millisecond of the CivilTime
with microsecond precision. It will have a value between 0 and 999.
The .nanosecond
property represents the sub-microsecond component of the microsecond of the CivilTime
with nanosecond precision. It will have a value between 0 and 999.
Creates a new CivilTime
object by overriding specified values to its members.
The specified values must be numeric if specified.
CivilTime.prototype.plus(values: DurationLike) : CivilTime
Creates a new CivilTime
object by subtracting values to its members.
The specified values must be numeric if specified. The specified values
must be numeric if specified and will be cast to a scaled integer (by calulating the absolute
value and flooring it).
The algorithm is such that:
- the individual values are added to the existing values.
- the range of
nanoseconds
is ensured to be between 0 and 999 by adjusting themicroseconds
- the range of
microseconds
is ensured to be between 0 and 999 by adjusting themilliseconds
- the range of
milliseconds
is ensured to be between 0 and 999 by adjusting theseconds
- the range of
seconds
is ensured to be between 0 and 59 by adjusting theminutes
- the range of
minutes
is ensured to be between 0 and 59 by adjusting thehours
- the range of
hours
is ensured to be between 0 and 23
CivilTime.prototype.minus(values: DurationLike) : CivilTime
CivilTime.prototype.withDate(date : CivilDate) : CivilDateTime
Combines this CivilTime
with the passed CivilDate
to create a new CivilDateTime
object.
.toString()
creates an ISO-8601 compliant string in the format:
hour
:minute
:second
.nanosecond
.
The hours
, minutes
, and seconds
are 0-padded to a minimum of 2 digits.
nanoseconds
is 0-padded to a minimum of 9 digits.
Equivalent to datetime.toString()
Creates a new CivilTime
by parsing an ISO-8601 string in the format created by .toString()
.
CivilDateTime
(and its siblings) represents a date and time corresponding to the requirements of ISO-8601.
This means specifically that it uses the proleptic Gregorian calendar whose days are 24 hours long and begin and end at midnight.
The constructor may only be called as such. It takes between 5 and 7 numeric arguments.
year
the Gregorian yearmonth
the Gregorian monthday
the Gregorian day of the monthhours
hour of the dayminutes
minutes of the hourseconds
seconds of the minutes (default: 0)milliseconds
milliseconds of the seconds (default: 0)microseconds
microseconds of the milliseconds (default: 0)nanoseconds
nanoseconds of the microseconds (default: 0)
The .year
property represents the year of the CivilDateTime
.
The .month
property represents the month of the CivilDateTime
.
The .day
property represents the day of the month of the CivilDateTime
.
The .dayOfWeek
property represents the day of the week where Monday is 1
and Sunday
is 7
in accordance with ISO-8601.
The .dayOfYear
property represents the ordinal day of the Gregorian year according to ISO-8601.
The .weekOfYear
property represents the ISO week-number. Beware that dates at the begining of a year may be part of a week from the preceding year, and dates at the end of a year may be part of a week at the beginning of the next year, as the first week of any year is defined as the week that contains the first Thursday of the week.
The .hour
property represents the hour of the CivilDateTime
.
The .minute
property represents the minute of the hour of the CivilDateTime
.
The .second
property represents the second of the minute of the CivilDateTime
.
The .millisecond
property represents the sub-second component of the second of the CivilDateTime
with millisecond precision. It will have a value between 0 and 999.
The .microsecond
property represents the sub-millisecond component of the millisecond of the CivilDateTime
with microsecond precision. It will have a value between 0 and 999.
The .nanosecond
property represents the sub-microsecond component of the microsecond of the CivilDateTime
with nanosecond precision. It will have a value between 0 and 999.
CivilDateTime.prototype.plus(duration: DurationLike) : CivilDateTime
Creates a new CivilDateTime
object by subtracting values to its members.
The specified values must be numeric if specified. The specified values
must be numeric if specified and will be cast to a scaled integer (by calulating the absolute
value and flooring it).
The algorithm is such that:
- the individual values are added to the existing values.
- the range of
nanoseconds
is ensured to be between 0 and 999 by adjusting themicroseconds
- the range of
microseconds
is ensured to be between 0 and 999 by adjusting themilliseconds
- the range of
milliseconds
is ensured to be between 0 and 999 by adjusting theseconds
- the range of
seconds
is ensured to be between 0 and 59 by adjusting theminutes
- the range of
minutes
is ensured to be between 0 and 59 by adjusting thehours
- the range of
hours
is ensured to be between 0 and 23 - the range of
days
is ensured to be between 1 and 29-31 depending on the month by adjustingmonth
- the range of
months
is ensured to be between 1 and 12 by adjusting theyears
.
CivilDateTime.prototype.minus(duration: DurationLike) : CivilDateTime
CivilDateTime.prototype.difference(other: TimeLike) : Duration
CivilDateTime.prototype.with(values: DateTimeLike) : CivilTime
Creates a new CivilTime
object by overriding specified values to its members.
The specified values must be numeric if specified.
CivilDateTime.prototype.withZone(ianaZone : string, filter?: string | symbol) : ZonedDateTime
CivilDateTime.prototype.withOffset(offset : string) : OffsetDateTime
CivilDateTime.prototype.getCivilDate() : CivilDate
CivilDateTime.prototype.getCivilTime() : CivilTime
CivilDateTime.prototype.getCivilYearMonth() : CivilYearMonth
CivilDateTime.prototype.getCivilMonthDay() : CivilMonthDay
.toString()
creates an ISO-8601 compliant string in the format:
hour
:minute
:second
.nanosecond
.
The hours
, minutes
and seconds
are 0-padded to a minimum of 2 digits.
nanoseconds
is 0-padded to a minimum of 9 digits.
Equivalent to datetime.toString()
Creates a new CivilTime
by parsing an ISO-8601 string in the format created by .toString()
.
A CivilYearMonth
is used to represent dates that have an unkown day component.
The constructor may only be called as such. It takes 2 numeric arguments.
year
the Gregorian yearmonth
the Gregorian month
CivilYearMonth.prototype.withDay(day: number) : CivilDate
CivilYearMonth.prototype.with(values: DateLike) : CivilYearMonth
CivilYearMonth.prototype.plus(value: DurationLike) : CivilYearMonth
CivilYearMonth.prototype.minus(value: DurationLike) : CivilYearMonth
CivilYearMonth.prototype.difference(other: CivilYearMonth) : Duration
Produces a string representation of the value in the format yyyy/mm
where yyyy
is the year with a minimum of 4 digits and a optional sign. and mm
is the months with a minimum of 2 digits.
CivilYearMonth.fromString(str: string) : CivilYearMonth
Parses a string in the exact format produced by CivilYearMonth.prototype.toString()
.
The CivilMonthDay
is used to represent dates that have an unknown year component.
Example:
The 25th of December
The 1st of January
Since no year is present calculations act as if any instances fall into non-leap-years.
The constructor may only be called as such. It takes 2 numeric arguments.
month
the Gregorian monthday
the Gregorian day of month
CivilMonthDay.prototype.withYear(year: number) : CivilDate
CivilMonthDay.prototype.with(values: DateLike) : CivilMonthDay
CivilMonthDay.prototype.plus(value: DurationLike) : CivilMonthDay
CivilMonthDay.prototype.minus(value: DurationLike) : CivilMonthDay
CivilMonthDay.prototype.difference(other: CivilMonthDay) : Duration
Duration objects are produced by subtracting two temporal object from each other using the minus()
method. Subtraction is limited to operations between objects of the same type.
All Duration
fields are integer values. Durations are immutable like all temporal objects.
Durations can only be created through the minus
method on temporal objects.
Is the integer number of years difference this interval represents.
Is the integer number of months difference this interval represents.
Is the integer number of day difference this interval represents.
Is the integer number of hours difference this interval represents.
Is the integer number of minutes difference this interval represents.
Is the integer number of seconds difference this interval represents.
Is the integer number of milliseconds difference this interval represents.
Is the integer number of microseconds difference this interval represents.
Is the integer number of nanoseconds difference this interval represents.
The difference operation implimented via the minus
method on temporal objects allow taking the difference between like objects, resulting in an Duration
. The specificity of the interval will depend on the objects involved.
Example:
- CivilDate - will produce an Duration with potential non-zero values in
years
,months
anddays
with all other fields nulled out. - CivilTime - will prodce an Duration with potential non-zero values in
hours
,minutes
,seconds
,milliseconds
,microseconds
andnanoeconds
with all other fields nulled out. CivilDateTime
- will produce an Duration where all fields are potentially non-zero
The addition operation is implemented via the plus
method on temporal objects. These methods take an DurationLike as an argument that is first converted to an actual Duration. That means that non-integer value in interval fields will be floored.
Adding an interval to a type means taking only the corresponding interval values and ignoring all others.
Example: CivilDate plus { hours: 48 } is a no-op since hours does not have a corresponding member to hours and they are therefore ignored.
Interfaces are not actually defined, they are simply conveniences to make conversing about temporal objects, methods and their arguments easier.
Any JS object that has date like properties. These are:
- year - a numeric representing the gregorian year
- month - a numeric representing the gregorian month
- day - a numeric representing the gregorian day
These properties are all optional, but if they are on a DateLike object they have to be numeric.
Any JS object that has time like properties. These are:
- hour - a numeric representing the hour of the day
- minute - a numeric representing the minute of the hour
- second - a numeric representing the second of the minute
- millisecond - a numeric representing the millisecond of the second
- microsecond - a numeric representing the microsecond of the second
- nanosecond - a numeric representing the nanoseconds of the microsecond
These properties are all optional, but if they are on a TimeLike object they have to be numeric.
Any JS object that is both a DateLike and a TimeLike.
Any JS object that has interval properties. These are:
- years - number of years difference
- months - number of months difference
- days - number of day difference
- hours - number of hours difference
- minutes - number of minutes difference
- seconds - number of seconds difference
- milliseconds - number of milliseconds difference
- microseconds - number of microseconds difference
- nanoseconds - number of nanoseconds difference
These properties are all optional, but if they are on an DurationLike object they have to be numeric.
[TOC]