-
Notifications
You must be signed in to change notification settings - Fork 156
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
Replace isLeapMonth with monthCode #1203
Comments
It was good to take a vacation from Temporal and come back to this issue with fresh eyes. TL;DR - If leap months are in the middle of the month order (like in the Hebrew calendar), then @sffc - do you know how other OSS lunisolar/lunar calendar libraries handle month numbering? Popping up a level, as we learn more about non-ISO calendars, we keep finding more ways where non-ISO fields are really different from ISO fields:
And we haven't even implemented a single lunar/lunisolar calendar yet! My guess is that once implementation is complete, we'll have identified even more ways that ISO fields and non-ISO fields don't align. My concern is that we'll continue to play whack-a-mole with each of these cases which could yield a sub-optimal, lowest-common-denominator API and/or put Temporal's schedule at risk. IMHO the root cause here is that we're sharing the same set of properties for what we now know can be quite different data: different values, different data types, additional properties, different expectations (e.g. years always go up), etc. @sffc - in the initial design of calendar features, did we consider having two sets of fields, e.g. Also, if fields can vary from what's expected in the vast majority of use cases, does that mean that well-written Temporal-using code must normalize the calendar (e.g. |
Who is "we"? Both in this statement and in the bullet points that follow? I've long said and understood that we should not try to draw parallels between the fields used by the ISO calendar and those used by human calendars. As you point out, human calendars have different needs from ISO calendars, and human calendars have different needs from each other, too. The only place where I think it is okay to draw parallels is in durations. A duration of 1 month or 1 year can be interpreted in ISO or in a human calendar. It's been almost a year since the Temporal champions decided on the current model of getters delegating to the calendar object. I have no problem with adding
No no no. The whole point is that you don't need to normalize the calendar system in most cases. The only time when you would need to do that is if you are writing logic that uses calendar-specific assumptions. If your code wants to assume that months are integer numbers contiguously increasing from zero, that is a calendar-specific assumption. But I claim that most code doesn't need to make such assumptions. Can you share examples of code that require ISO-specific assumptions that don't have a better way of being written in Temporal? Note: When designing ZonedDateTime, I originally advocated that it didn't have these getters, precisely because they open up an avenue for writing code that isn't calendar-safe. But, we decided that these getters were useful enough to justify the risk. |
I mean all of us champions! ;-) I definitely didn't mean to blame you or anyone individually-- this stuff is hard! Here's what I meant:
Good idea. Below is a list of assumptions that may not be true in some calendars. I'm sure that this list is incomplete, so feel free to add more! In one case (see #1220) I wasn't able to figure out a calendar-safe version because we lack a That said, my overall concern is not that there's no way to solve these use cases correctly; rather, it's that solving them correctly will be unintuitive or un-ergonomic for many developers, leading to so much "wrong" code in the ecosystem that introducing non-ISO data into an app will break existing code or libraries that the developer can't easily fix.
const inLaterYear = (date, reference) => date.year > reference.year;
// vs.
const inLaterYear = (date, reference) => date.year !== reference.year && Temporal.PlainDate.compare(date, reference) > 0;
const { month: number } = date;
// vs.
const { month } = date;
if (typeof month !== 'number') throw new TypeError('This code only works with numeric months');
for (let month = 1; month < monthsInYear; month++) {
printOneMonth(date.with({ month}).toYearMonth());
}
// vs.
// This one stumped me. Specifically, how to get the first day of the year
// if we can't depend on `month` being numeric and starting with `1`?
const startOfYear = date.with({ month: 1, day: 1 });
for (let i = 0; i < date.monthsInYear - 1; i++) {
printOneMonth(date.add({ months: i });
}
const isNextMonthInSameYear = (date, reference) => date.month === reference.month + 1;
// vs. (note that `===` fails if month is not a primitive)
const isNextMonthInSameYear = (date, reference) => reference.add({ months: 1 }).month === date.month;
function thisDateInHistory(date) {
historicalStuff = [];
for (let i = 0; i < 100; i++) {
date = date.subtract({ years: 1 });
historicalStuff.push(getHistoryThing(date));
}
return historicalStuff;
}
// vs. (never increment years one-at-a-time)
function thisDateInHistory(date) {
historicalStuff = [];
for (let i = 1; i <= 100; i++) {
historicalStuff.push(getHistoryThing(date.subtract({ years: i })));
}
return historicalStuff;
}
getBirthdayThisYear = (monthDay) => monthDay.toPlainDate({
year: Temporal.now.plainDate(monthDay.calendar)
});
// vs. (FWIW, I'm not sure that this is the best solution)
getBirthdayThisYear = (monthDay) => {
const today = Temporal.now.plainDate(monthDay.calendar);
const birthday = today.with(monthDay.getFields(), calendar: undefined);
return birthday;
}
|
Hebrew month numbering is an issue I/we have been aware of for quite some time; see quite a bit of previous discussion in tc39/proposal-intl-displaynames#55. In that thread, it seems like I was mostly talking to an empty room; I could have probably done more to solicit opinions. This new thread here, where I recommend using strings instead of numbers as the month identifiers, has largely arisen from unicode-org/icu4x#355. In other words, I see this thread as a potential conclusion of a year-long investigation of how to do month identifiers in ECMAScript. It's simply not true that this is an example of "as we learn more, we're bumping into new issues". Ethiopian time aside (which wasn't in my original proposal), I don't have a whole lot of reason to believe that there are any major pitfalls that we're unaware of at this point. Thanks for the examples. My main takeaway is that you can make ISO-specific assumptions to make code shorter, but the most correct, expressive option in most cases is already available in Temporal. My favorite is how to increment forward and backward between months: one might think that you can do Except for the items you identified, like the inability to go to the first day in a month or the first month in a year, I think your calendar-friendly code is correct. getBirthdayThisYear is a little clunky; here's a slightly cleaner version: getBirthdayThisYear = (monthDay) => {
const today = Temporal.now.plainDate(monthDay.calendar);
return Temporal.PlainDate.from({ ...today.getFields(), ...monthDay.getFields() });
} |
IMHO the underlying problem is: where should Temporal set the frontier between a set of fields that is nice to compute with, and a set of fields that correspond to the most common expression of a date. Let me illustrate this, taking the first date the Julian calendar was enforced. All historians say: 1 Jan. 45 B.C. Ideally, if you want to be totally unambiguous, you specify the Julian Day (at noon) for this date: 1704987. Very precise, but not very handy to remenber not to use. You'd like something more "human-readable". If you use So you'd like to write it You could even, with the same This pattern works for all solar calendars, including coptic and ethiopic with their epagomenal days: the author may consider those days as building up a 13th month, but skip this 13th month when adding up months in DateAdd. The author, not Temporal, is the calendar's specialist. For lunisolar calendars, my hint would be to add an optional field, I am not aware of any (algorithmic) calendar for which the first day of the first (non leap) month is not the first date for the year. Should this happen, the author should tailor Temporal should make no assumption about what |
@Louis-Aime - Thanks so much for the helpful feedback! Your suggestion for Based on what I understand about this problem at the moment, your proposal seems reasonable. I'm going to recapitulate it below to make sure I understand:
Is this correct? Please correct anything I missed or got wrong.
Could you explain this a little more, ideally with some code examples of how you'd expect epagomenal days to work with various Temporal APIs? Also, if those days are not a 13th month, then what would be the value of
This is good to know. If I understand you correctly, #1220 is not a problem because the code to get the start of year is always |
Makes sense. I'm glad we're digging into these issues now, and I apologize if I wasn't listening earlier! I admit that for some non-ISO issues it's not been clear (to me at least) which problems must be resolved in Temporal and which are 402-only and don't affect Temporal. Is there a border that's explicitly defined anywhere?
Even if problems have been known for a while, I've usually found that defining specific solutions (what we're doing now) and later implementing those solutions is likely to expose more issues. It's possible that we might get lucky and not find anything new, but that's not the pattern I usually observe for other thorny problems. For example, we knew about the "Brazil cancels DST" problem for a long time, but the solution to that problem had unexpected ripple effects in other parts of the ZonedDateTime API that would have been really hard to predict. So I think it's important to try to wrap up the design ASAP if possible, and to try to get even basic prototype implementations of non-ISO calendars so we can validate those solutions with real code.
Yep, I think a good TL;DR for the docs is that One important note: I opened #1223 for documenting best practices like the one above. |
Thank you @justingrant for commenting. The main problem we all have - I mean man has - is that the word "month" covers several different concepts. Unlike "day", and even if "today" here may not be "today" there, we may add or substract "days" with any calendar and find the same result. The Iso calendar does not define the "month" in general and for all calendar, it only defines a nice way human-readable way to designate the universal reality of "day". "Month" can only be understood in the context of a specific calendar. Generally speaking, lunar and luni-solar calendar use lunar month (this is the etymological sense...) whereas solar calendars call month one 12th of the tropical year. In all calendars, adding or subtracting days may be done the same way, so Temporal can do it. On the other hand, each calendar defines how to handle month, and finally Temporal should make no general assumption. It is ok to give a framework for "monthDay", or "yearMonth", but it is up to each calendar author to fill it. And I feel that the present version of Temporal is really good for that. Let's go through your recap.
|
It may or may not be relevant here, but the modern and traditional Hebrew calendars differ on how they define the first month of the year (see Wikipedia). In the modern Hebrew calendar, Tishrei is the first month, and in traditional, Nisan is first. I guess in the proposed Temporal model, both calendars would be different Temporal.Calendar implementations, and they would just number their months differently. Also, note that by using the month/monthType model, one invariant we definitely lose is that |
Temporal offers a way of hiding the difficulties of calendrical computations. The only way to add durations, even to add a single day, is using dateAdd(), which is defined with the calendar. The only way to compute a duration between two dates is to use the dateUntil() method defined in the calendar. The calendar's author will have to decide how to handle the issue of adding one month after Jan. 30, which is not the same thing as adding 30 days. IMHO, no assumption should be made on year, month and day components. And this should be explained in the documentation. After all, in the very simple calendar used in Rome, the day after October 4, 1582 is October 15, 1582. The last day of October 1582 is 31 but the number of days is 21. For most German (protestant) cities, year 1700 began as a leap year, but finished as a common year. The year had only 355 days, and one week number is missing. February's last day was the 18th. This is just because the switching date to Gregory was ISO 1700-03-01. This works fine with Temporal. Of course, it took me some time to tune dateAdd and dateUntil in the general case of calendars that switch to Gregorian, like all calendars did in Western Europe. But the naive user can add month in all cases, even if sometimes one of the added months is only 20 days long. A "nice to have" feature is having the possibility to specify negative years. With the moke-up I specified with Temporal, you may enter either IMHO, the main constraint is that customised fields should map CLDR parts. If, for instance, the calendar's author wants Adar we displayed in a different way when it is Adar I in an embolismic year, the corresponding month index should be different. May be someone should try specifying a lunisolar calendar as a Temporal custom calendar, in order to see what's happening ? I could do that by the new (Gregorian, not Milesian) year. BTW, the herefore mentionned calendar (julian, german, english etc.) are still usable with Temporal at https://louis-aime.github.io/Temporal-experiences/. |
I think it's good that we are discussing this, but I also think we should not get too far into conclusions before the other i18n experts at Google are available to consult with, as we've mentioned on a mail thread. |
After spending a few days implementing several non-ISO calendars, I now have a recommendation for how to solve this problem. FWIW, it turned out that Hebrew is the easy case! ;-) Adar I (the leap month: 6 in the modern order, 12 in the biblical order) can just be skipped in non-leap years, kinda like how Feb 29 is skipped in non-leap years in Gregorian calendars. True, the non-leap Adar is called "Adar" in non-leap years and "Adar II" in leap years, but Temporal doesn't care about names so this shouldn't be a problem. So if the leap month is numbered 6 (and is skipped in non-leap years), then the A more problematic case is Chinese where leap months can be injected anywhere other than the first or last month. So if we wanted to have the same behavior as Hebrew, we'd need to add additional sparse month numbers for a total of 22 possible numbers! Currently, Hindu seems even more problematic, because months can be duplicated, removed, and or merged (!!!). From http://cldr.unicode.org/development/development-process/design-proposals/chinese-calendar-support:
Given all this complexity, it seems like there are (at least) four options for month indexing:
IMHO, (2) is the best because its problems seem the easiest to work around. Developers who are writing cross-calendar apps or who unexpectedly encounter a non-ISO date can follow a few simple rules and otherwise can ignore the month issue. Developers who are knowingly dealing with lunisolar calendars can sidestep Anyway, that's my recommendation. What do you think? If we adopt (2), then the following guidance would probably be sufficient:
Did I miss any other guidance that we'd need to document for this case? One thing I'm unsure about is how the |
BTW, if we do decide to go with (2) above for handling month numbering, we may want to consider something similar for years too: the It'd also avoid the weirdness with negative-incrementing eras like BC where adding one year makes the year later in AD but earlier in BC. |
Thanks for this analysis! I had previously reached the conclusion that (1) was the best overall option. I suggested numbers for calendars with the same months every year, and strings (not object literals) as the data type for calendars with uneven months, in large part so that (2) does have some nice properties; in particular, it's easy to define regardless of calendar system. I'm concerned about it though because the only invariant you really have is that adjacent month numbers are adjacent months in a certain year, but you should be using (3) and (4) are other valid alternatives to consider if we decided that we want to force months to be numeric. You didn't include (5), which was Louis-Aimé's suggestion to make the month number correspond to the month in a normal year, and a separate monthType flag to distinguish between months in special years. This works very well for Chinese; it would probably also work in Hebrew. In Hindu, the calendar could arbitrarily choose the first of the two month numbers if the months are merged, and use the separate monthType flag if the months are split. |
Oops, I just edited my comment above to add it. Thanks for the reminder. That said, I'm not a fan of (5) because IMHO the most important invariant is equality. If
The insight that convinced me in favor of (2) was realizing that there are two basic cases: either you know and understand the calendar you're working with, or you don't. If you know the calendar, then the guidance to use custom fields instead of But if you don't know lunisolar calendars (the vast majority of developers don't) then IMHO (2) is easiest to explain and learn because the guidance is so simple: a) avoid cross-year month comparisons and b) don't display the month number in a UI. The other four options seemed like they required much more complicated guidance and required the developer to learn much more about how lunisolar calendars work. For users who don't understand lunisolar calendars to start with, simpler guidance and less learning is important. I'm also sympathetic to this point of view because a week ago I didn't understand lunisolar calendars either. While evaluating each of these options I tried to imagine my week-ago self trying to read docs and write code for each of them. (2) was the only one that I thought that my week-ago self could easily understand without having to spend an hour or two on Wikipedia learning about lunisolar calendars. ;-) Anyway, back to (1): my main concern with strings is that developers who don't know lunisolar calendars will never think to prepare for a string value. It's just too unexpected, so lunisolar-calendar-using end users will end up with crashes that they can't fix. It'd also make the IDE/TS story much harder. Also combining strings with numbers has unexpected behavior, e.g.
I approached this with the same "know vs. don't know lunisolar" lens as above. If you know lunisolar calendars, you'll quickly learn that you need to use two fields to set the month. Per above, once you learn that you need two fields, then IMHO it's not a big deal what the name of the second field is. But if you don't know lunisolar calendars, then you won't know how to pick a month number anyways-- except for month 1 for start-of-year use cases which per @Louis-Aime above we can assume is always a "normal" month. So I'm not too worried about
After thinking about this, I think the |
Thank you to @justingrant for the analysis. At least we all know the pros and cons of every method or recommendation we should choose. If a monthDay is specified as a leap day or as a day in a leap month, it should fall back as monthDay of a common year. Normally, this should be specified by the calendar's author. As far as I have experimented, for Feb. 29th or for the intercalary day of the Milesian calendar (last day of last month), Temporal already takes the day before. This is indeed the traditional way of celebrating the anniversaries for people born on Feb. 29th (like Giacomo Rossini). With this principle, a day in Adar II should be replaced by the same day in Adar of common hebrew years. However, the documentation is not very clear with this. |
I'll add more notes later, but here's a TL;DR from today's meeting discussion on this topic, including a short follow-up 1:1 discussion with @gibson042. There are three pieces of data about non-ISO months:
(a) is helpful for various non-human-readable use cases, like being able to know if a month is the next or previous month to another month in the same year. (b) and (c) together are needed as input to any localization API that displays the name or other human-readable representation of the month, e.g. "4bis" in Chinese dates like IMHO, any solution should make it easy for developers to get all three pieces of data, either by offering 3 total fields ( @ryzokuken clarified that the complex Hindu calendar discussed here isn't actually implemented in ICU. Instead the @gibson042 noted that https://tools.ietf.org/html/rfc7529#section-4.2 explains how iCalendar deals with non-ISO months. My reading of the iCalendar spec is that this system (adding an "L" after the normal month for any intercalary months) is OK for uniquely expressing a non-ISO date (e.g. for calendar recurrence patterns) but it doesn't provide the three pieces of metadata noted above. Specifically, (b) and (c) are not present, and arguably (a) isn't present either. We agreed that the next step is to prepare a table that lists various purposes and use cases and describes how well each of the 5 options above would address those use cases. A quick list of cases include:
I'll add more use cases to the list and will try to transform the result into a design doc that can be PR-ed into the public docs. |
I'll make a radical proposal. Use strings for all months in all calendars, even ISO! Valid values are "1" through "13" and "1L" through "12L". Advantages:
Reference: Frank Tang's proposal Intl.DisplayNames V2 Let's look at how it works for @justingrant's use cases.
So, it's basically a slam dunk, right? 🙃 |
I think it's great to explore possibilities like this. I see challenges with this proposal but I like the out-of-box thinking. IMHO the biggest blocker is that developers usually think of YMD values as numbers. It will be easier to teach developers a few rules about lunisolar calendars (e.g. don't compare month numbers across years, or don't expect month numbers to be consecutive) than to train them to stop committing bugs like this: // will always return false if month is a string
const isFirstMonth = (date) => date.month === 1;
// will always return false
deepEqual({year: date.year, month: date.month, day: date.day}, {year: 2020, month: 10, day: 30});
Not sure that the month code is enough. How would DisplayNames know whether the non-leap month name is "Adar" or "Adar II" ? Don't you have to provide the year too?
If we're creating month codes, it does seem like making them sortable is a good idea via the leading zero. Although this breaks compat with iCalendar codes.
The more I think about this use case, the more I'm unsure about it. Could you give a few real-world examples of this cross-year comparison case, specifically using I'm asking because all the cases I can think of are cases where === won't actually help. For example: I signed up for an annual subscription that bills on the first day of the month. Just because I signed up during Adar I doesn't mean I can skip paying next year! Instead of ===, an API call is needed for that case to tell me when I need to pay next year. Kinda like if my birthday is the Islamic equivalent of Feb 29 and I need to know when to buy a cake next year. Here's a counter-proposal:
To clarify for future readers (like my week-ago self before I learned about all these calendars): month numbering is only a problem for lunisolar calendars, of which Hebrew, Chinese, and Traditional Korean (Dangi) are the only built-in ones AFAIK. Other non-ISO calendars (islamic, persian, ethiopic, buddhist, etc.) work fine with regular numeric months. |
One idea of Temporal is to make it possible to create custom calendars. This is necessary in particular for the "real" calendars used in Europe, with a switching date to Gregorian. I do not know any solar calendar that could not work with Temporal's present version. Islamic (lunar) calendars work too. |
Temporal already allows calendar authors to add one or more custom fields with any name(s) and any data type(s) the calendar author thinks is appropriate. Calendar authors also already have the ability to customize how addition/subtraction works, so if a particular calendar's addition semantics will skip over leap months, that's already supported. But IMHO there's a lot of value in calendars following a set of invariants that make cross-calendar code possible and that simplify the process of localizing new and existing code and making that code reliable. For reasons I discussed above, I strongly believe that one of those invariants should be that the Do you think this is good enough? |
Here's an update on a few of the notes above:
I now think that the leading zero would be a bad idea. If one of the main reasons for
@sffc - LMK what use cases you had in mind for cross-year lunisolar month comparisons. I'm still stumped. Still having trouble finding common use cases for
FWIW, neither of these approaches work with today's polyfill. String-typed months like |
Can you share insight on where you found that "1L" is compatible with iCalendar but "01L" is not? By my reading of RFC 7529, it looks like the leading zero is legal (see "05L" below):
I claim this invariant is useful in the same situations where a Temporal.Month type would be useful. We didn't include Temporal.Month in large part because you can just use the month ID.
Sorry, can you point to the list of reasons why you believe that invariant is important?
Having both |
Yep! My suggestion is to have both
99%+ of Temporal usage won't involve lunisolar calendars. AFAIK there's no country on Earth that primarily uses a lunisolar calendar for civil or business purposes. These calendars' primary uses seem to be dating religious holidays and other cultural events. Optimizing for niche use cases seems like the wrong choice if it makes the overall Temporal API harder to use or buggier (e.g. #1203 (comment)). I don't think we should optimize for pints, fortnights, cubits, or sixpence either. ;-) That said, I think
What I'm not OK with is having a string value be returned from the
This use case sounds good in theory, but real-world cases (e.g. when to celebrate my birthday this year? When does my subscription renew? How long will my lease last?) don't generally skip over years where a day (e.g. Feb 29) or a month (e.g. Adar I) doesn't exist. Instead, some convention is used to ensure that the event is still recognized even if the leap day or leap month is not present. Therefore, I don't see how any implementation of The use case of "is there a Feb 29 or Adar I this year?" is still valid and should be supported (and would be supported via
I built sales reporting apps for 7+ years in my last job. Even though this was for Western companies, one worldwide truism is that companies go through great lengths to compare apples to apples in financial reporting. For example, some global companies like PepsiCo measure "months" as 4-week/4-week/5-week periods (https://en.wikipedia.org/wiki/4%E2%80%934%E2%80%935_calendar) instead of the actual months so that they can better compare weekday to weekday sales. In other words, due to the unpredictability of months in particular, companies will often tweak the civil calendar to get better and more consistent reporting. A month that only happens every few years on an irregular pattern is the epitome of "less consistent"-- so I'd be very surprised if many companies chose to report this way. Furthermore, the main reason why you would want non-solar-month-specific sales reporting is if there are holidays during that month (e.g. Ramadan) which could affect sales. But leap months almost by definition are unlikely to have major holidays in them because humans love to have holidays every year! Finally, (given that lunisolar months are the only ones where this issue matters) I'm not aware of any lunisolar calendar that's widely used for business reporting. Israel uses a lunisolar calendar for religious purposes but AFAIK business reporting in Israel is done on Gregorian because the country's business sector is closely tied to global markets. China AFAIK does business reporting in Gregorian for similar reasons. From https://www.timeanddate.com/calendar/about-chinese.html:
I don't mean that there will never be a case for this kind of reporting. For example, I could see companies wanting to answer questions like "do leap months tend to have lower or higher sales because there are no major holidays during them?" but those are unusual cases that could be handled by a separate Feel free to suggest other cross-year use cases. I'm not saying there aren't any, only that the only ones I've been able to think of (including the ones above) seem more like edge cases than mainstream behaviors that we'd need to optimize defaults for. |
Sorry I realized after posting above that I didn't respond to a few things. Fixing that below.
Hmm, interesting. I was mostly thinking of the other parts of that spec that never show the leading zero, e.g.
But if the format like
I believe that "number" is more important than "sortable". Excerpting from #1203 (comment):
"Sortable" is also helpful because most developers will assume that a month 13 is chronologically later than a month 9. Reversing that will cause bugs that only show up in lunisolar calendars. Ditto for consecutivity. Most developers will assume that months start with 1 and consecutively increase to Therefore, I don't think that the relatively rare case of lunisolar calendars is worth breaking those invariants for the vast majority of use cases. Especially when the alternative (use |
After a few weeks writing a lot of non-ISO calendar code, here's some cases where I've seen a month getter being quite useful. Some of these work with both a numeric and a string value. Others work only with a numeric value.
Almost all of above have workarounds that don't rely on a month getter. But the code snippets above are perhaps the most obvious and intuitive ways to solve those use cases, especially for developers who are relatively unfamiliar with Temporal. |
Conclusion from 2021-01-19 meeting:
|
Minor clarification:
|
After documenting this change, I realized that year may not be needed in all PlainMonthDay.from cases. If the calendar property is undefined (which will probably be the most common case) the code is declaring that it's not cross-calendar code and that it's using the ISO calendar. So why require year in that case? Instead, I think a better solution would be to only require year if the calendar field is also present. This would limit the ergonomic impact to only the cases where cross-calendar code is possible. Any objections? If not I can update my docs PR #1321 to make year required only if calendar is present. |
* remove examples of using the constructors for non-ISO calendars * revise warning text for the constructors * make `year` optional in `from()` if `calendar` is not used (see tc39#1203)
This is consistent with assuming ISO in |
* remove examples of using the constructors for non-ISO calendars * revise warning text for the constructors * make `year` optional in `from()` if `calendar` is not used (see #1203)
To be precise about the last bit:
|
Agree. Given that this type only has two fields,
Yep. If The calendar should apply additional validation, e.g. to ensure that the provided |
My thoughts exactly! (disregard what I wrote here earlier, I figured out how we could have the cake and eat it too) |
That seems not quite right to me; Edit: Disregard me, I think I follow. I thought I'd misread the comment 4 days ago, but I was correct reading it then and incorrect now. |
Yep, the high-level goal is to encourage developers to write code that will work across calendars. If the user doesn't add |
monthCode may be given in addition to or instead of month, and for PlainMonthDay, monthCode must be given if year is not given. Closes: #1203
monthCode may be given in addition to or instead of month, and for PlainMonthDay, monthCode must be given if year is not given. Closes: #1203
monthCode may be given in addition to or instead of month, and for PlainMonthDay, monthCode must be given if year is not given. Closes: #1203
monthCode may be given in addition to or instead of month, and for PlainMonthDay, monthCode must be given if year is not given. Closes: #1203
@justingrant and I were discussing this on chat. If the ISO calendar is being used, programmers can make certain assumptions about the return value of getters, particularly
.month
, that don't work in other calendar systems. In lunar calendars, month numbers do not have the same invariants as in ISO-like calendars.This made me notice that we currently document that
.month
benumber
. I'd rather it beany
, and each calendar chooses what type to return there.@justingrant pointed out: "code that uses getters or
with
is bound to fail if confronted with a non-ISO calendar." That's right: there's not really such a thing as calendar-agnostic code that uses getters orwith
. Getters orwith
should be used only if you know what the calendar is.Alternatively, if we require
.month
to be a certain type like anumber
or other primitive, we should document exactly what that means: whether there are invariants we can enforce that work across calendar systems.While we're talking about this, I think it's worth lifting the
number
restriction from all fields, not just.month
.https://github.com/tc39/proposal-temporal/blob/main/docs/calendar.md#methods
The text was updated successfully, but these errors were encountered: