From 8ad09a548c1e8f19b0983ddc829dc62128aad8d7 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Wed, 12 Aug 2020 11:42:55 -0700 Subject: [PATCH] Add explainer for LocalDateTime I intend to eventually adapt this into the documentation for LocalDateTime, but given the draft status of the API this seems more appropriate. --- docs/buildDocs.js | 2 +- docs/localdatetime-draft.md | 101 ++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 docs/localdatetime-draft.md diff --git a/docs/buildDocs.js b/docs/buildDocs.js index 234a3b0f3e..e97ba3594e 100644 --- a/docs/buildDocs.js +++ b/docs/buildDocs.js @@ -7,7 +7,7 @@ const loadLanguages = require('prismjs/components/'); const encoding = 'utf-8'; -loadLanguages(['bash']); +loadLanguages(['bash', 'typescript']); // From marked/helpers.js const escapeReplacements = { diff --git a/docs/localdatetime-draft.md b/docs/localdatetime-draft.md new file mode 100644 index 0000000000..be46fddf3c --- /dev/null +++ b/docs/localdatetime-draft.md @@ -0,0 +1,101 @@ +# Zoned Date/Time Type + +**Name:** It's currently being referred to as `Temporal.LocalDateTime`, but we already know this is not a good name for reasons discussed years ago. +This name is not intended to stick. + +**What it is:** The LocalDateTime type is intended to refer to an event, that happened (or will happen) in a particular place. + +**Motivation:** +- "Event that happened in a particular place" is a very common use case, perhaps the most common. + So we want a type that does the right thing for that use case. +- Both Absolute and DateTime have cases where they do the right thing for this use case, and cases where they do the wrong thing. + (ex.: "Move a meeting three days later" is correct with DateTime and wrong with Absolute; "Postpone a reminder by one hour" is correct with Absolute and wrong with DateTime.) +- The cases where Absolute and DateTime do the wrong thing will only produce the wrong result when used across a DST transition, so in many places the bugs will only pop up twice per year, if at all. +- When writing the cookbook, to deal with this use case, we found that it was actually necessary a lot of the time to pass around a record consisting of `{ absolute, timeZone }`. + If that is so common, then we may as well have a type for it. +- Even worse, there is a temptation to pass around a serialized string instead of such a record, which is wrong because the time zone offset may have changed when you read in the string again with `Temporal.Absolute.from()`. + +**API**: The API looks something like this: + +```typescript +class Temporal.LocalDateTime { + // creation + constructor(absolute: Temporal.Absolute, timeZone: Temporal.TimeZone, calendar: Temporal.Calendar); + static from(item: string | object, options?: object) : Temporal.LocalDateTime; + + // immutable 'mutation' + with(localDateTimeLike: object, options?: object) : Temporal.LocalDateTime; + withCalendar(calendar: Temporal.Calendar) : Temporal.LocalDateTime; + get startOfDay() : Temporal.LocalDateTime; + + // getters / field access + get era(): string | undefined; + get year(): number; + get month(): number; + get day(): number; + get hour(): number; + get minute(): number; + get second(): number; + get millisecond(): number; + get microsecond(): number; + get nanosecond(): number; + get timeZone(): Temporal.TimeZone; + get timeZoneOffsetNanoseconds(): number; + get timeZoneOffsetString(): string; + get calendar(): Temporal.Calendar; + get dayOfWeek(): number; + get dayOfYear(): number; + get weekOfYear(): number; + get hoursInDay(): number; + get daysInMonth(): number; + get daysInYear(): number; + get isLeapYear(): boolean; + get isTimeZoneOffsetTransition(): boolean; + getFields(): object; + getISOCalendarFields(): object; + + // type conversion + toAbsolute(): Temporal.Absolute; + toDateTime(): Temporal.DateTime; + toDate(): Temporal.Date; + toYearMonth(): Temporal.YearMonth; + toMonthDay(): Temporal.MonthDay; + toTime(): Temporal.Time; + + // comparison + static compare(one: Temporal.LocalDateTime, two: Temporal.LocalDateTime): number; + equals(other: Temporal.LocalDateTime): boolean; + + // arithmetic + plus(duration: Temporal.Duration | object, options?: object) : Temporal.LocalDateTime; + minus(duration: Temporal.Duration | object, options?: object) : Temporal.LocalDateTime; + difference(other: Temporal.LocalDateTime, options?: object) : Temporal.Duration; + + // serialization / presentation + toString() : string; + toJSON() : string; + toLocaleString(locales?: string | Array, options?: object): string; +} +``` + +(This is not a fully functional description, it's made concise for readability. +For example, the details of the options bags are omitted.) + +It basically has all the API of DateTime, but with a time zone as well, adding the time zone offset for disambiguating near transitions, and several time-zone-related convenience properties. +But since it represents an unambiguous moment in time (like Absolute, and unlike DateTime), the internal model is more like Absolute with a time zone and calendar. + +To create one, you use `Temporal.LocalDateTime.from`, or the `toLocalDateTime()` method of another Temporal type. +- From a string: `from(string)` +- From raw DateTime fields: `from({ year, month, day, etc., timeZone, timeZoneOffsetNanoseconds })` +- From an Absolute: `absolute.toLocalDateTime(timeZone)` +- From a DateTime, which potentially needs disambiguation: `dateTime.toLocalDateTime(timeZone, { disambiguation })` + +As in the other Temporal types, the constructor is more low-level and takes an Absolute, a TimeZone, and a Calendar. + +"New" API that is not on DateTime: +- `hoursInDay` - this is not possible with Absolute (no concept of days and no time zone) nor with DateTime (no time zone), but it makes sense for this type. +- `timeZoneOffsetNanoseconds` - without this, you'd need to do `ldt.timeZone.getOffsetNanoseconds(ldt.toAbsolute())`. +- `timeZoneOffsetString` - ditto, you'd need `ldt.timeZone.getOffsetString(ldt.toAbsolute())`. +- `startOfDay` - this is a convenient way to get the start of the day, especially if the day does not start at 00:00 due to DST. + Also not needed with Absolute (no concept of days) nor with DateTime (day always starts at 00:00), but it makes sense for this type. +- `isTimeZoneOffsetTransition` - convenience property getter.