-
Notifications
You must be signed in to change notification settings - Fork 152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Write cookbook of practical scenarios using Temporal #240
Comments
As promised earlier today, this is finally ready. It remains my hope that filling in the bodies of the below functions is used to validate the Temporal API, and in particular that any discomfort identified is used to change that API before it is finalized. This should have happened months ago, but I still believe that it is better late than never. ConstructionInstant from legacy DateMap a legacy ECMAScript function getInstantAtEs2019DateInstance( es2019Date ) {
// ???
} Zoned instant from instant and time zoneMap a function getParseableIanaZonedStringAtInstant( absolute, ianaTimeZoneName ) {
let timeZoneObject = getTimeZoneObjectFromIanaName( ianaTimeZoneName );
return getParseableZonedStringAtInstant(absolute, timeZoneObject);
}
function getParseableZonedStringAtInstant( absolute, timeZoneObject ) {
// ???
}
getParseableIanaZonedStringAtInstant(
Temporal.Absolute.from("2020-01-09T00:00Z"),
"Europe/Paris"
).replace(/(T\d+:\d+).*?([+-])/, "$1$2") ===
"2020-01-09T01:00+01:00[Europe/Paris]"; Fixed-offset instant from instant and UTC offsetMap a function getParseableFixedOffsetStringAtInstant( absolute, utcOffset ) {
// ???
}
getParseableFixedOffsetStringAtInstant(
Temporal.Absolute.from("2020-01-09T00:00Z"),
"+09:00"
).replace(/(T\d+:\d+).*?([+-])/, "$1$2") ===
"2020-01-09T09:00+09:00"; TimeZone instanceMap an IANA time zone name into an object encapsulating all corresponding offsets and transition rules. function getTimeZoneObjectFromIanaName( ianaTimeZoneName ) {
// ???
} Construct such an object directly from tzdata-compatible rules of arbitrary complexity (e.g., for use in testing). function getTimeZoneObjectFromRules( rules ) {
// ???
} SortingZoneless datetimesSort an array of zoneless function getSortedLocalDateTimes( datetimes, direction ) {
// ???
} Absolute instantsSort an array of strings (each of which is parseable as a function getSortedInstants( parseableAbsoluteStrings, direction ) {
// ???
} Time Zone ConversionPreserving local timeMap a zoneless date and time of day into a function getInstantWithLocalTimeInZone(
dateTime,
timeZoneObject,
disambiguationPolicy = "constrain" ) {
// ???
} Preserving absolute instantMap a zoned date and time of day into a string serialization of the local time in a target zone at the corresponding instant in absolute time. This could be used when converting user-input date-time values between time zones. function getParseableZonedStringAtInstantWithLocalTimeInOtherZone(
sourceDateTime,
sourceTimeZoneObject,
targetTimeZoneObject,
sourceDisambiguationPolicy = "constrain" ) {
let instant = getInstantWithLocalTimeInZone(
sourceDateTime,
sourceTimeZoneObject,
sourceDisambiguationPolicy
);
return getParseableZonedStringAtInstant(instant, targetTimeZoneObject);
}
getParseableZonedStringAtInstantWithLocalTimeInOtherZone(
Temporal.DateTime.from("2020-01-09T00:00"),
getTimeZoneObjectFromIanaName("America/Chicago"),
getTimeZoneObjectFromIanaName("America/Los_Angeles")
).replace(/(T\d+:\d+).*?([+-])/, "$1$2") ===
"2020-01-08T22:00-08:00[America/Los_Angeles]"; UTC offset for a zoned event, as a stringMap a function getUtcOffsetStringAtInstant( absolute, timeZoneObject ) {
// ???
}
getUtcOffsetStringAtInstant(
Temporal.Absolute.from("2020-01-09T00:00Z"),
getTimeZoneObjectFromIanaName("America/New_York")
) === "-05:00"; UTC offset for a zoned event, as a number of secondsMap a function getUtcOffsetSecondsAtInstant( absolute, timeZoneObject ) {
// ???
}
getUtcOffsetSecondsAtInstant(
Temporal.Absolute.from("2020-01-09T00:00Z"),
getTimeZoneObjectFromIanaName("America/New_York")
) === -18000; Offset between two time zones at an instantMap a function getUtcOffsetDifferenceSecondsAtInstant(
absolute,
sourceTimeZoneObject,
targetTimeZoneObject ) {
// ???
}
getUtcOffsetDifferenceSecondsAtInstant(
Temporal.Absolute.from("2020-01-09T00:00Z"),
getTimeZoneObjectFromIanaName("Etc/UTC"),
getTimeZoneObjectFromIanaName("America/Chicago")
) === -21600; ArithmeticUnit-constrained duration between now and a past/future zoned eventMap two function getElapsedDurationSinceInstant(
absoluteThen,
absoluteNow,
largestTimeElementInResult = "year" ) {
// ???
}
(result => `${result.sign}${result.duration}`)(
getElapsedDurationSinceInstant(
Temporal.Absolute.from("2020-01-09T00:00Z"),
Temporal.Absolute.from("2020-01-09T04:00Z")
)
) === "+PT4H"; Nearest offset transition in a time zoneMap a function getInstantOfNearestOffsetTransitionToInstant(
absolute,
timeZoneObject,
directionAndClusivity ) {
// ???
} Comparison of an instant to business hoursCourtesy @gilmoreorless at https://github.com/tc39/proposal-temporal/issues/26#issuecomment-513208398 , map a localized date and time of day into a time-sensitive state indicator ("opening soon" vs. "open" vs. "closing soon" vs. "closed"). function getBusinessOpenStateText(
absoluteNow,
timeZoneObject,
businessHours,
soonWindowDuration ) {
// ???
} Flight arrival/departure/durationCourtesy @kaizhu256 at https://github.com/tc39/proposal-temporal/issues/139#issuecomment-510925843 , map localized trip departure and arrival times into trip duration in units no larger than hours. function getTripDurationInHrMinSec( parseableDeparture, parseableArrival ) {
// ???
} Map localized departure time and duration into localized arrival time. function getLocalizedArrival( departureAbsolute, duration, destinationTimeZoneObject ) {
// ???
} "A month from now"Map a function plusMonths( date, months, disambiguationPolicy = "constrain" ) {
// ???
} Push back a launch dateAdd the number of days it took to get an approval, and advance to the start of the following month. function plusAndRoundToMonthStart( date, delayDays ) {
// ???
} Schedule a reminder ahead of matching a record-setting durationMap a function getInstantBeforeOldRecord(
startAbsolute,
previousRecordDuration,
noticeWindowDuration ) {
// ???
} Nth weekday of the month#240 (comment) function getWeeklyDaysInMonth( yearMonth, dayNumberOfTheWeek ) {
// ???
}
getWeeklyDaysInMonth(new Temporal.YearMonth(2020, 2), 1).join(" ") ===
"2020-02-03 2020-02-10 2020-02-17 2020-02-24";
getWeeklyDaysInMonth(new Temporal.YearMonth(2020, 2), 6).join(" ") ===
"2020-02-01 2020-02-08 2020-02-15 2020-02-22 2020-02-29"; Given a Temporal.Date instance, return the count of preceding days in its month that share its day of the week. function countPrecedingWeeklyDaysInMonth( date ) {
// ???
}
countPrecedingWeeklyDaysInMonth(Temporal.Date.from("2020-02-28")) === 3;
countPrecedingWeeklyDaysInMonth(Temporal.Date.from("2020-02-29")) === 4; IsolationAttenuationCreate an object that supports exactly the same interface as function getAttenuatedTemporal( Temporal, attenuations ) {
// ???
} ExtensionExtra-expanded yearsCreate a Temporal derivative that supports arbitrarily-large years (e.g., +635427810-02-02) for astronomical purposes, ideally without requiring modifications to year-agnostic interfaces such as Time (but still supporting e.g. function makeExpandedTemporal( Temporal ) {
// ???
} |
Thanks for the cookbook! FYI, of the 18 examples here, I believe only 3 require calendar information:
This matches with my instinct that we should support calendar systems but we don't need to reshape the whole API around them. Rather, we can introduce a calendar field in places it is needed, but without adding any radically new types. |
Just posting here as FYI: we have an internal client who wants to do something that is difficult with the current JavaScript Date class. The client wants to display midnight in an arbitrary IANA timezone in the user's local timezone. For example, initialize a datetime as midnight in America/Chicago, and then display it in America/Los_Angeles. This is difficult in JavaScript Date because it doesn't support IANA timezones. This could be another cookbook example. |
Good news, this is composable from the existing recipes! I've added it as an explicit scenario with implementation: Preserving absolute instant |
* Addition of cookbook * Adding parsableZonedString * * Cookbook link in readme * Prettier did some tidying up * Update cookbook/absoluteFromLegacyDate.mjs Co-Authored-By: Richard Gibson <[email protected]> * Update cookbook/absoluteFromLegacyDate.mjs Co-Authored-By: Richard Gibson <[email protected]> * Update cookbook/absoluteFromLegacyDate.mjs Co-Authored-By: Richard Gibson <[email protected]> * Update cookbook/absoluteFromLegacyDate.mjs Co-Authored-By: Richard Gibson <[email protected]> * Update cookbook/getParseableZonedStringAtInstant.mjs Co-Authored-By: Richard Gibson <[email protected]> * * update the cookbook examples to have no import * fix index.mjs to add Temporal to the global objectr * update readme * adding note about how to run cookbook files Co-authored-by: Richard Gibson <[email protected]>
I think this was accidentally closed by the "Addition of cookbook fixes #240" commit message, in any case there are still more cookbook examples to add, so let's keep this open! |
Another use case from an internal client. They want to:
I will continue posting use cases here as we get them. They come organically from internal email lists and question boards. |
How would they define whether a time zone is "in summer time"? I suppose it depends on the location of the time zone (northern/southern hemisphere) as well as whether that time zone does a daylight saving time transition at all? My understanding is also that time zones can have offset transitions for other reasons than daylight saving. |
DST is not a universal concept. There are places that don’t have anything like it; there are those that have multiples. Also it’s not even the case that one offset correlates to astronomical time (noon = highest sun) in any way. So whether a time is DST or not is actually pretty meaningless. TL;DR “Is this summertime” is a meaningless question |
I agree with @pipobscure that the question becomes meaningless when it's dug into. 1. Why do they need to know if it's in "daylight time"?
2. "Daylight saving time" is NOT the same as "summer time".This discussion has been circling on the tzdb mailing list for a few years now (where there's a suggestion to redefine "DST" as "daylight shifted time"). The real catch comes from countries that define their timekeeping laws to say that "standard time" is in summer, with "alternative time" (or some other name) in winter. That is, they have negative DST. The main examples of this are Ireland (+01 "standard" in summer, +00 in winter), Nigeria, and Morocco. Morocco is a particularly interesting case because they recently changed from +00/+01 to a "permanent" +01... except they still need to switch back to +00 during the month of Ramadan. See also these threads in tzdb list archives from the past year or so:
The most relevant quote comes from the last thread listed above (specifically from this reply):
|
It appears the client is trying to interop with the win32 function SetDynamicTimeZoneInformation, which requires a struct containing the following fields, which all appear to be required: typedef struct _TIME_DYNAMIC_ZONE_INFORMATION {
LONG Bias;
WCHAR StandardName[32];
SYSTEMTIME StandardDate;
LONG StandardBias;
WCHAR DaylightName[32];
SYSTEMTIME DaylightDate;
LONG DaylightBias;
WCHAR TimeZoneKeyName[128];
BOOLEAN DynamicDaylightTimeDisabled;
} DYNAMIC_TIME_ZONE_INFORMATION, *PDYNAMIC_TIME_ZONE_INFORMATION; So in order to interop with this API, you need to know which offset ("bias") is for daylight time and which offset is for standard time. Probably not super important for ECMAScript, but worth pointing out. The existence of this win32 API suggests that this distinction between which offset is daylight or not may also appear in other places in the tech ecosystem. |
Taken from @gilmoreorless
|
This one is a bit different, since it runs in the cookbook page directly in the browser. See: #240
Another in-browser example, this one is inspired by an HTML form on timeanddate.com. See: #240
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Use cases from #561 suggested by @justingrant Closes: #561 See also: #240
Use cases from #561 suggested by @justingrant Closes: #561 See also: #240
Use cases from #561 suggested by @justingrant Closes: #561 See also: #240
Issues have now been opened for all the discussions raised about changing the API based on writing the cookbook examples. Here are the cookbook examples that haven't been written yet, that would be good to have. In particular for exercising the custom time zone and custom calendar APIs when they are merged into main. Once those are written I think we can close this issue, or else close it now and open separate issues for those three.
|
Hooray! +1 for opening a new issue for the remaining cases, and close this issue as fixed. We (you) deserve it! |
I apologize for not having time for Temporal lately, but I wanted to address the big comment here in advance of reviewing the full cookbook.
Yes, the cookbook
I don't think polyfill implementation issues should block cookbook recipes coded against the documented API, but presumably this was fixed by #513 anyway.
Yes, the interesting aspects of this recipe are the disambiguation.
I agree,
Excellent; this is precisely the kind of insight that we were hoping would be provided by having the cookbook. I'm currently inclined towards both Temporal.ZonedDateTime and signed Temporal.Duration, although the latter comes with serialization/deserialization concerns because ISO 8601 durations are unsigned (resulting in some libraries supporting/preferring negative-valued time elements such as a) PT1H-210M while others support/preferring only a single leading negation such as b) -PT2H30M or conceivably even c) P-T2H30—I prefer the latter model, c specifically if there's a gun to my head, but all of the syntax changes make me uncomfortable because 8601 is very particular about designators). |
In the meantime I did write a cookbook example that does this: https://github.com/tc39/proposal-temporal/blob/main/docs/cookbook/getInstantWithLocalTimeInZone.mjs (It became possible after adding
Yes, this was fixed.
These are covered by, respectively:
I'm not sure I agree that it's critical to have a cookbook recipe for something we couldn't figure out a use case for. But let's continue that discussion in a new issue?
Prior discussion in #237, current feedback thread in #592.
|
@gibson042 suggested doing this, and I think it's a great idea that shouldn't be lost. A couple purposes:
Resources for Moment and date-fns, including documentation and stack overflow questions, could be good seed material
The text was updated successfully, but these errors were encountered: