Proposal-temporal: How to deal with calendars with YearMonth and MonthDay?

Created on 17 Nov 2019  Â·  27Comments  Â·  Source: tc39/proposal-temporal

These types were added as a response to #55. However, as we've been finding in #262, these types present problems when it comes to internationalization.

More than any of the other Temporal types, these types are inherently rooted in the Gregorian calendar system. There is no correct way to convert "November 17th" or "November 2019" into another calendar system for the purpose of displaying it to the user.

I argue that by putting these as first-class Temporal types, we are encouraging programmer behavior that is inherently bad for i18n on the web. I am not convinced that the use cases for these types are strong enough to outweigh the i18n costs associated with them. If users really want them, they can just make { year, month } or { month, day } records.

Can you list out more clearly what are the use cases for YearMonth and MonthDay, and why we need them as first-class types?

@rxaviers @littledan @younies @pipobscure

calendar

Most helpful comment

Temporal.YearMonth corresponds to the HTML month concept, which is specifically proleptic-Gregorian (and used by <input type=month>). And IIRC, Temporal.MonthDay came along with it on general principle (and also for obvious use cases involving holidays, anniversaries, and similar Gregorian-annual events).

Neither seem _fundamental_ to Temporal, though.

All 27 comments

We first started thinking down these lines in response to the TAG review in https://github.com/w3ctag/design-reviews/issues/311 . Although Temporal.Time, Temporal.DateTime and Temporal.Date should be enough for <input> elements (EDIT: this comment seems to have been mistaken, see https://github.com/tc39/proposal-temporal/issues/264#issuecomment-554850729), but for the <time> tag, there are some parsed microformats that correspond to Temporal.YearMonth or Temporal.MonthDay. Thanks to @slightlyoff for the reference; cc @cynthia for the TAG review in general.

This may be my fault, where I encouraged @pdunkel to actively resolve the issues raised in the TAG review, not realizing that this particular one is a rather obscure edge case. We actually still haven't really completed the requested study of integration--no one has proposed a particular integration with the <time> element; we just added the type since it looked like it would be useful there.

Based offline communication with @alice, my understanding of the recommended approach for working with the Web will be to provide the detailed DOM integration once Temporal really catches on, rather than doing this all pre-emptively. Probably that means that we should focus on the types that we think JS programmers will find useful, rather than adding more complexity just for the DOM integration. We can add additional types later if we find they are needed.

From what I know now, I'd be open to removing this type.

Why is the concept of month and day rooted to the Gregorian calendar? In the Hebrew calendar, for example, Hanukkah falls on the 25th of Kislev, which id represent with a MonthDay in a non Gregorian calendar, and then convert to the Gregorian one for a given year. Many holidays have a MonthDay with no year - that seems like an important use case to me.

YearMonth describes, for example, how LinkedIn tracks employment on profiles. This has use cases too.

The restriction to the Gregorian calendar exists throughout the Temporal proposal. It's unfortunate, but it's not clear to me if it would be tractable to permit all of this calendar arithmetic in a more generalized way.

It's possible for something to have important use cases and still not be included in the MVP. Where should we draw the line here?

Why is the concept of month and day rooted to the Gregorian calendar? In the Hebrew calendar, for example, Hanukkah falls on the 25th of Kislev, which id represent with a MonthDay in a non Gregorian calendar, and then convert to the Gregorian one for a given year. Many holidays have a MonthDay with no year - that seems like an important use case to me.

The concept of month and day is not rooted to the Gregorian calendar, but Temporal.MonthDay is defined based on it, because Temporal.MonthDay has an equivalence relation with the Gregorian-based ISO string. In other words, Temporal.MonthDay does not (currently) give a mechanism for saying "25th of Kislev".

The restriction to the Gregorian calendar exists throughout the Temporal proposal. It's unfortunate, but it's not clear to me if it would be tractable to permit all of this calendar arithmetic in a more generalized way.

Given the triplet year, month, and day in the Gregorian calendar, we have enough information to project the date into most/all* calendar systems we care about. So, although the representation of Temporal.Date is Gregorian, it doesn't prevent us from converting to other calendars.

However, if you remove one of the three parts of the triplet, there is no longer enough information to perform the conversion. That's why YearMonth and MonthDay are problematic.

* Days in the Hebrew calendar start at sunset, but, as discussed in #262, it appears to be acceptable when converting dates to/from Hebrew to pretend as if the days started approximately 6 hours later at midnight.

One other note: I think it is reasonable for us to put forward the ISO calendar (Gregorian) as the standard for representing dates and performing computations on them, like the metric system is the standard for units of measurement. That's what java.time has already done, and which we are following with Temporal. However, we should at least view other calendar systems as something we support for output representation to the user. It's like if you store a road distance in kilometers, but display it in miles to users who prefer the imperial system. We can store dates in Gregorian, but display them in Islamic to users who prefer that calendar system.

i support dropping Temporal.MonthDay (and Temporal.Time). for me, the whole point of this proposal is to improve usability wrt:

  1. [year-based] date-arithmetic
  2. [utc-based] datetime-arithmetic
  3. conversions between utc <-> localtime at the UI/presentation layer

MonthDay and Time don't fall into any of the above scopes -- and honestly have terrible usability compared to just using MM-DD and hh:mm:ss isostrings.

Temporal.YearMonth corresponds to the HTML month concept, which is specifically proleptic-Gregorian (and used by <input type=month>). And IIRC, Temporal.MonthDay came along with it on general principle (and also for obvious use cases involving holidays, anniversaries, and similar Gregorian-annual events).

Neither seem _fundamental_ to Temporal, though.

@aphillips for W3C i18n.

I'm happy to drop YearMonth and MonthDay. They were added in response to compatibility with <input> elements and since they are frequently used concepts. They are as @gibson042 says NOT integral / fundametal to Temporal.

Ca we come to a decision here?

Sounds like the participants in this thread are up for dropping them. I'd be in favor. Let's give this a day or so for any more comments, and then we can make a PR to remove it. Of course, we'll review this decision at a future TC39 meeting.

I believe they should be kept.

@ljharb You've mentioned some use cases above, but also further feature requests that MonthDay does not support, and which we don't have any particular idea for how to support. What does that mean for what we should do going forward?

I don’t have any feature requests there; just an example conceptual use case that isn’t currently supported. MonthDay is useful in the Gregorian calendar for any holiday or annual repetition that’s based on the month and day number, like Christmas or Valentine’s Day or New Year’s. YearMonth is useful for any scenario that focuses on a specific year and month, like LinkedIn’s or similar.

max/min isn’t a pattern we should be replicating here, so while “integral to temporal” is certainly a qualifier, it should not be a disqualifier.

In the same vein as #268, one possible way to keep these types in an i18n-friendly way would be to introduce a calendar option, such as,

let date = Temporal.now.date();  // a Temporal.Date
let monthDay = date.getMonthDay("hebrew");  // a Temporal.MonthDay
monthDay.calendar;  // "hebrew"
monthDay.month;  // 2, i.e. Heshvan
monthDay.toString();  // TypeError: cannot convert hebrew calendar to ISO string

let yearMonth = YearMonth.with({ month: 11, year: 2019 });  // TypeError: calendar is required
let yearMonth = YearMonth.with({ month: 11, year: 2019, calendar: "iso" });  // OK
yearMonth.toString();  // OK

// Get the calendar system for the current locale:
let dtf = new Intl.DateTimeFormat();
let calendar = dtf.resolvedOptions().calendar;
dtf.format(date.getMonthDay(calendar));  // properly localized month-day string

This would also address @ljharb's concern in #ljharb about being able to represent "25th of Kislev".

@sffc I think that'd be a much bigger project... We've been developing Temporal with the idea that the data model would be based on the Gregorian calendar. I don't think there's been a full design proposed for something more general.

@ljharb

max/min isn’t a pattern we should be replicating here

Could you explain why not? Even if we keep these features, and add non-Gregorian calendars, there will be many meaningful date/time calculations that we still leave out.

Apologies for misreading the thread earlier. It might just be too early to make a strong decision here before we have feedback from the polyfill. As a way of working through our lack of consensus, what if we do the following:

  1. The champion group makes a provisional call for what to include
  2. They continues to develop polyfills, documentation, and spec text
  3. They make another an npm release of the polyfill, and collect feedback, including looking into possible issues caused by the lack of these features.

    • Advocates of YearMonth and MonthDay could further make their case by making a package for these, on top of Temporal, and collecting feedback on that package

  4. Following that feedback, and after making any changes deemed appropriate based on the experience on npm, Temporal gets proposed at TC39 for further feedback and seeing committee consensus on Stage 3

@sffc I think that'd be a much bigger project... We've been developing Temporal with the idea that the data model would be based on the Gregorian calendar. I don't think there's been a full design proposed for something more general.

I proposed a design in #268.

@sffc Thanks, I had missed that.

@littledan max/min leaves a lot of holes in the language that userland will attempt to patch in different ways. In this case, I'm concerned about developers not being forced to "pick placeholder values" to represent the concept of YearMonth or MonthDay, and causing bugs as a result. For example, both twitter ads and airbnb had a datepicker that was broken at one time because it used "midnight" as the placeholder time on a Date to represent what Temporal.Date would otherwise represent, and in Brazil during the DST transition, midnight doesn't exist. Devs shouldn't have to "just know" that noon is a safe placeholder, and similarly, there's immense value in having a nominal type to convey developer intention instead of ambiguously and implicitly conveying that with a placeholder value.

Since it looks like we're moving forward with having a calendar slot in Temporal objects, it will now be possible to express YearMonth and MonthDay in an i18n-friendly way. There is still an open question about how to do the toLocaleString, but that's being discussed in #262. Closing this issue.

Actually, I'll re-open this thread to answer one more question: unlike Temporal.Date[Time], it is not possible to convert a Temporal.YearMonth or Temporal.MonthDay between calendar systems, unless we establish some kind of "overlap" scheme. Do we just not have .withCalendar() methods on Temporal.YearMonth and Temporal.MonthDay?

I would say that conversion is up to the calendar. Specifically any calendar has to be able to convert things to the ISO calendar or throw if the conversion is impossible.

I'd be fine with leaving off .withCalendar() on YearMonth and MonthDay, given that we know it will usually not work. I don't think there's any mandate that these objects need to have all the methods of Date that we can possibly apply an interpretation to.

A situation I want to avoid is one where you get exceptions in some locales but not others. If you get a Temporal.MonthDay in a lunar calendar without any well-defined conversion to the ISO calendar, an exception must be thrown, and most developers won't realize that.

Conclusion, Jan 20: remove toISO() and withCalendar() methods from Temporal.MonthDay and Temporal.YearMonth.

FYI: The data model agreed in #290 raises some additional questions about the MonthDay data model, which I described in #391.

I think we've reached a conclusion on this issue. The new data model proposed in #391 should make YearMonth and MonthDay fully compatible with arbitrary calendar systems.

Was this page helpful?
0 / 5 - 0 ratings