feat: Temporal dates and times#4759
Draft
kerams wants to merge 2 commits into
Draft
Conversation
kerams
commented
Jul 10, 2026
Comment on lines
+7
to
+33
| declare global { | ||
| namespace Temporal { | ||
| class PlainDate { | ||
| constructor(isoYear: number, isoMonth: number, isoDay: number); | ||
| static compare(one: PlainDate, two: PlainDate): number; | ||
| readonly year: number; | ||
| readonly month: number; | ||
| readonly day: number; | ||
| readonly dayOfWeek: number; | ||
| readonly dayOfYear: number; | ||
| add(duration: { years?: number, months?: number, days?: number }): PlainDate; | ||
| until(other: PlainDate): Duration; | ||
| equals(other: PlainDate): boolean; | ||
| } | ||
| class Duration { | ||
| readonly days: number; | ||
| } | ||
| namespace Now { | ||
| function plainDateISO(): PlainDate; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| export type DateOnly = Temporal.PlainDate; | ||
|
|
||
| export const PlainDate = Temporal.PlainDate; | ||
| export type PlainDate = Temporal.PlainDate; |
Contributor
Author
There was a problem hiding this comment.
This will go (likewise in the other file) once we upgrade to Typescript 6 or 7.
kerams
commented
Jul 10, 2026
Comment on lines
+130
to
+196
| export function parse(str: string): DateOnly { | ||
| function fail(): DateOnly { | ||
| throw new Exception(`String '${str}' was not recognized as a valid DateOnly.`); | ||
| } | ||
|
|
||
| // Allowed separators: . , / - | ||
| // TODO whitespace alone as the separator | ||
| // | ||
| // Whitespace around separators | ||
| // | ||
| // Allowed format types: | ||
| // yyyy/mm/dd | ||
| // mm/dd/yyyy | ||
| // mm/dd | ||
| // mm/yyyy | ||
| // yyyy/mm | ||
| const r = /^\s*(\d{1,4})(?:\s*[.,-\/]\s*(\d{1,2}))?\s*[.,-\/]\s*(\d{1,4})\s*$/.exec(str); | ||
| if (r != null) { | ||
| let y = 0; | ||
| let m = 0; | ||
| let d = 1; | ||
|
|
||
| if (r[2] == null) { | ||
| if (r[1].length < 3) { | ||
| if (r[3].length < 3) { | ||
| // 12/30 = December 30, {CurrentYear} | ||
| y = Temporal.Now.plainDateISO().year; | ||
| m = +r[1]; | ||
| d = +r[3]; | ||
| } else { | ||
| // 12/2000 = December 1, 2000 | ||
| m = +r[1]; | ||
| y = +r[3]; | ||
| } | ||
| } else { | ||
| if (r[3].length > 2) | ||
| fail(); | ||
|
|
||
| // 2000/12 = December 1, 2000 | ||
| y = +r[1]; | ||
| m = +r[3]; | ||
| } | ||
| } else { | ||
| // 2000/1/30 or 1/30/2000 | ||
| const yearFirst = r[1].length > 2; | ||
| const yTmp = r[yearFirst ? 1 : 3]; | ||
| y = +yTmp; | ||
|
|
||
| // year 0-29 is 2000-2029, 30-99 is 1930-1999 | ||
| if (yTmp.length < 3) | ||
| y += y >= 30 ? 1900 : 2000; | ||
|
|
||
| m = +r[yearFirst ? 2 : 1]; | ||
| d = +r[yearFirst ? 3 : 2]; | ||
| } | ||
|
|
||
| if (y > 0) { | ||
| try { | ||
| return new Temporal.PlainDate(y, m, d); | ||
| } catch { | ||
| return fail(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return fail(); | ||
| } |
Contributor
Author
There was a problem hiding this comment.
This parse (likewise in the other file) is only necessary if we want to closely follow .NET's parse behaviour. We could decide to remove it and instead delegate to from, which should still be close enough and acceptable in the vast majority of cases.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements #4751.
Opt-in with
--test:js-temporal, which definesFABLE_COMPILER_JAVASCRIPT_TEMPORAL.DateOnlyasPlainDateTimeOnlyasPlainTimeDateTimeasPlainDateTimeextended with kind metadataDateTimeOffsetasZonedDateTimewith an offset-based time zone rather than IANATimeSpanasDurationAll these types have nanosecond precision, whereas the old representation truncates everything below milliseconds (partly due to
Date), which is something that bit me in the past. However, the precision in .NET is a tick (100ns), so the current implementation is truncating nanosecond values on construction and value access.