This is an implementation of a polyfill for the TC39 Temporal Proposal.
This is a moving implementation. It is currently changing along with the proposal. Sometimes it will implement suggestions to try out that have not yet been agreed. Use with caution!!!
Run node with Temporal enabled:
node --no-warnings --experimental-modules --loader ./test/resolve.source.mjs -r ./test/global.config.js -i for running against source.
or
node --no-warnings --experimental-modules --loader ./test/resolve.compiled.mjs -r ./test/global.config.js -i for running against the bundled version.
Run tests:
npm run test:source to run the tests against source
or
npm run test:compiled to run the tests against the bundled version.
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
signis either+or-hoursis the hours offset 0-padded to 2 digits.minutesis 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
nanosecondsis ensured to be between 0 and 999 by adjusting themicrosecond - the range of
microsecondsis ensured to be between 0 and 999 by adjusting themillisecond - the range of
millisecondsis ensured to be between 0 and 999 by adjusting thesecond - the range of
secondsis ensured to be between 0 and 59 by adjusting theminute - the range of
minutesis ensured to be between 0 and 59 by adjusting thehour - the range of
hoursis ensured to be between 0 and 23 by adjustingday - the range of
daysis ensured to be between 1 and 29-31 depending on the month by adjustingmonth - the range of
monthsis 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:00offset
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/Viennatimezone1976-11-18T15:23:30.123456789+01:00[Europe/Berlin]- created withEurope/Berlintimezone
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.
yearthe Gregorian yearmonththe Gregorian monthdaythe 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
daysis ensured to be between 1 and 29-31 depending on the month by adjustingmonth - the range of
monthsis 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.
hourshour of the dayminutesminutes of the hoursecondsseconds of the minutes (default: 0)millisecondsmilliseconds of the second (default: 0)microsecondsmicroseconds of the millisecond (default: 0)nanosecondsnanoseconds 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
nanosecondsis ensured to be between 0 and 999 by adjusting themicroseconds - the range of
microsecondsis ensured to be between 0 and 999 by adjusting themilliseconds - the range of
millisecondsis ensured to be between 0 and 999 by adjusting theseconds - the range of
secondsis ensured to be between 0 and 59 by adjusting theminutes - the range of
minutesis ensured to be between 0 and 59 by adjusting thehours - the range of
hoursis 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.
yearthe Gregorian yearmonththe Gregorian monthdaythe Gregorian day of the monthhourshour of the dayminutesminutes of the hoursecondsseconds of the minutes (default: 0)millisecondsmilliseconds of the seconds (default: 0)microsecondsmicroseconds of the milliseconds (default: 0)nanosecondsnanoseconds 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
nanosecondsis ensured to be between 0 and 999 by adjusting themicroseconds - the range of
microsecondsis ensured to be between 0 and 999 by adjusting themilliseconds - the range of
millisecondsis ensured to be between 0 and 999 by adjusting theseconds - the range of
secondsis ensured to be between 0 and 59 by adjusting theminutes - the range of
minutesis ensured to be between 0 and 59 by adjusting thehours - the range of
hoursis ensured to be between 0 and 23 - the range of
daysis ensured to be between 1 and 29-31 depending on the month by adjustingmonth - the range of
monthsis 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.
yearthe Gregorian yearmonththe 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 DecemberThe 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.
monththe Gregorian monthdaythe 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,monthsanddayswith all other fields nulled out. - CivilTime - will prodce an Duration with potential non-zero values in
hours,minutes,seconds,milliseconds,microsecondsandnanoecondswith 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]