-
Notifications
You must be signed in to change notification settings - Fork 105
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
Accessing the sequence of eras of a calendar #598
Comments
@sffc @Manishearth - what's the latest status (if any) of an enumeration API for calendar eras? @devongovett - #541 is the issue currently tracking which Japanese eras will be supported in Temporal and what their identifiers will be. You should probably watch that issue. Keep in mind that the decision about which eras to support is out of scope of the Temporal spec. This doesn't matter from an end-user standpoint, but it matters somewhat for who in the TC39 world is actually making era-related decisions: the 402 working group, not the Temporal champions. BTW, @Manishearth wrote a document that goes into a lot of detail about the problem of creating era identifiers for Japanese. Manish, are there any updates from what's in that doc that you'd want to share? As for why we don't use numeric identifiers for eras, the main reason was readability. But also, for Japanese, new historical eras are being discovered from time to time, so any numbering scheme would have to either be sparse (to allow new ones to be injected) or would have to be out-of-order. Neither of those is a great experience, so using alphanumeric strings was considered better because consecutive, sequential-across-time identifiers wouldn't be expected. @sffc or @Manishearth can correct or expand on this if I got it wrong. BTW, if you want to find years of eras, if you have an enumeration then it's trivial to create a Temporal.PlainDate for the first
Note that Japanese eras that start mid-year are interesting. The way Temporal handles this (at least in the polyfill currently) is that It's an interesting question of whether an era enumeration API should include the specific dates where the era starts and ends. This seems like a reasonable requirement to add to whatever TC39 proposal targets that enumeration use case. |
Thanks, that's useful information. I didn't realize that new historical eras are still being discovered. In that case, string identifiers with a way to enumerate the valid values definitely seems like a better solution.
It seems I don't have access to this.
Indeed. Determining the number of years in an era isn't so simple with this in mind. For the date picker use case, the maximum allowed value for the year field should change depending on both the era as well as the month and day fields. For example, in the
For my use case, either a specific method to get the number of years in an era, or providing the exact dates and letting me compute that myself would work. |
No updates, the doc is as far as we've gotten.
Feel free to request access; unfortunately my employer's settings don't let me link-share docs. |
@devongovett - I'm going to transfer this issue over to the https://github.com/tc39/ecma402/ repo which would be the place where the actual decision would live about how to handle this enumeration use case. Here's a starting point suggestion for requirements of this API, although as the user of it I'd defer to you to edit/change these suggestions to match your use case. Use Case Summary
Proposed API Requirements 402 experts - The list below is a rough initial idea. Feel free to replace with a better solution!
{
era: string,
displayName: string,
startDate: Temporal.PlainDate | null, // or ISO string instead?
endDate: Temporal.PlainDate | null, // or ISO string instead?
}
|
Oops, I don't have permission to transfer this issue to the 402 repo. @sffc is this something you can do? |
@justingrant thanks for your detailed thoughts! Overall it seems like your proposal would cover my use cases. Just confirming: do you think this would be better as a top-level Similarly, |
This seems like a good fit for a future addition to the Intl.Enumeration proposal? |
What's the difference between Intl.Enumeration and Intl.DisplayNames? When would a use case go into one vs. the other? |
@devongovett I don't have an opinion about the right long-term home of accessing era-related metadata, but I suspect that @sffc and/or @ptomato may have an opinion.
I would not support an API that returned a scalar number of years in an era, because it would seem to complicate use cases where eras start or end mid-year (which is most eras, AFAIK). If the era starts in the 8th month of one year and ends in the 3rd month of another year, should it include or exclude the partial years on either end? Instead, if we do offer an API to fetch metadata about a specific era, then IMHO it should return two PlainDate instances that represent the first and last day of the era. Then the caller can pull out whatever info is needed. Ideally, the result of a "get metadata for one era" API would have the same shape as the elements returned by the proposed enumeration API, so callers could either ask for metadata about one era or could enumerate through all of them, but could use the same code to process one era's metadata. Another alternative is not to offer this kind of single-era API at all, and instead require callers to filter the results of the proposed enumeration API. Given that era metadata is somewhat of a niche use case, this may be an OK workaround. |
BTW, instead of thinking of this as an enumeration API, another alternative could be to think of it as a "Get Calendar Info" API which would return additional metadata about the calendar beyond eras, e.g.:
I'm not saying this is better or worse than a plain enumeration API, but it may be something to consider. |
I like the idea of using Enumeration to list calendar IDss, and each calendar object describing itself, rather than trying to make an Enumeration API for each property of a calendar. |
Enumeration is for IDs, DisplayNames is for localized, human-readable names. |
+1 to making this a method on |
Ahh, got it. I had assumed that there was a was a method on the DisplayNames V2 prototype to get all the localized text values in a single call, but I was mistaken. BTW, given that showing a UI picker is the canonical use case for enumeration, and given that UI pickers require localized text for each option, I opened tc39/proposal-intl-enumeration#36 to understand the reasoning for not also offering a more ergonomic way to fetch localized text for all IDs in one method call. |
2022-02-10 discussion: https://github.com/tc39/ecma402/blob/master/meetings/notes-2022-02-10.md#accessing-the-sequence-of-eras-of-a-calendar-598 Conclusion: Revisit this issue as part of a bigger proposal about calendar display names or datetime picker components. |
The Temporal polyfill had to build an internal "enumerate all eras for a calendar" API. Based on that experience, below is a suggestion for the metadata required for each era. My assumption is that an enumeration API would return an array that's ordered chronologically. (Not sure why we'd need an iterator instead of an array here.) FWIW, the most recent era is always used more than older eras, so having the list be sorted in reverse chronological order may make it easier to work with because the current era would always be interface Era {
/**
* Non-localized string ID of the era, e.g. "reiwa" or "bce". Only intended for programmer use,
* not for display to end users. Valid characters are: a-z (lowercase only), 0-9, and `-`.
* */
id: string;
/**
* Earliest day of this era. If undefined, this is the oldest era and has no lower bound (e.g. BC).
* It should be in the era's calendar. If the user needs the ISO date, it's easy to convert using `withCalendar`.
* */
startDate?: Temporal.PlainDate;
/**
* Latest day of this era, inclusive. If undefined, this era is the most recent era and has no upper bound.
* It should be in the era's calendar. If the user needs the ISO date, it's easy to convert using `withCalendar`.
* */
endDate?: Temporal.PlainDate;
/**
* If true, this era counts years backwards like BC. There can be at most one era with
* `reverseYears===true` for each calendar, and if present it must be the oldest era.
* */
reverseYears: boolean;
} BTW, there are other metadata that the polyfill uses internally that probably are not needed in a public API because they can be easily derived from the metadata above:
const hasYearZero = era => (era.reverseYears ? era.endDate : era.startDate).year === 0
const getAnchorYear = eras => eras.find(e => e.startDate.year === e.startDate.eraYear)?.year; |
New non-array.prototype APIs are generally expected to return an iterator, not an array - that’s why matchAll does. |
It makes sense that But could you remind me why arrays are discouraged for cases where the number of results is known to be small and the enumerated data is built-in and immutable? In all ICU calendars except Japanese, the maximum numbers of eras is three. (In Japanese, it will be a few hundred elements, which is also small.) And the underlying era data will almost certainly be a native array before it's wrapped in an iterator to send back to the client. What's the benefit of requiring implementers and callers to write extra code to deal with an intermediate iterator? BTW, here's a few era-list use cases that I know about:
const eraNames = new Intl.DisplayNames(['en'], { type: 'era', calendar: 'japanese' });
const dropdownData = eras.map(e => ({ value: e.id, label: eraNames.of(e) }));
meiji = eras.find(e => e.id === 'meiji');
eras[0];
eras.filter(e => Math.round(e.startDate.withCalendar('iso8601').year / 100) === 15); For each of these cases, an iterator version of |
That's a fair argument; just be prepared for the pushback. |
At this point I'm mostly just curious: what's the advantage of iterators for cases where the underlying data is synchronous and small? If there's a significant advantage then the extra hassle of |
I'm not arguing that there is one; i agree with you. |
Do you know what people who would argue for iterators would say? I'm sure there must be a good reason, just trying to understand what it is. |
Also, unrelated to iterator vs. array, another thing to think about is whether era iteration would be supported for custom calendars. My assumption would be "no" because we don't support localization for custom calendars nor timezones today. Enumerating eras of a custom calendar may have limited value without the ability to also localize the names of those eras, which would be another new API. |
I asked delegates about this. Conclusion:
TL;DR - returning an |
I'm curious about the decision to use string identifiers for the
era
field. There are some cases where it might be useful to be able to manipulate the era field similarly to other numeric fields. For example, a date picker UI control might want to allow the user to change the era using the arrow keys. This example is from the macOS native date picker control on the Japanese calendar.Screen.Recording.2021-07-20.at.6.52.07.PM.mov
In ICU and Joda Time (I believe), the era field is a number like other fields, which means it can be incremented and decremented. But this is not possible with Temporal because the era field is a string, and not included in Duration.
I do like that eras are more user-friendly by using identifiers though (an enum would also work). Perhaps an alternative would be for calendars to have a method to retrieve the list of (or at least the previous and next) available era identifiers. This way, UI code could find the current era identifier, and determine the next/previous one from there.
I also think a way to retrieve the number of years in an era would be useful. For example, a date picker UI might wish to make the year field wrap back to the first year when the end of an era is met (so that editing one field does not affect another). This could be a method on calendar similar to the other methods for retrieving limits.
The text was updated successfully, but these errors were encountered: