mermaid.js
First of all, my apologies for confusing the issue on Tuesday by using the jargon "data-driven exceptions". I thought this was a more widely known term in TC39, but I guess we only use it in Temporal meetings. We will take an action item to come up with a better name for this design principle to avoid confusion in the future. I'll give an overview of how data processing is considered in Temporal, to clear up the confusion from yesterday, then at the end I'll present the normative change again.
The context behind "avoid data-driven exceptions" is that date/time data contains lots of weird edge cases. There are leap days, daylight saving time transitions, but also things like non-Gregorian calendars where there are leap months. So it will be common when writing Temporal code that developers will test with the normal cases but forget to test with those weird cases. If the weird cases throw exceptions, then code will work fine in the lab but will break when confronted with actual real-world data in production. Code that breaks when confronted with valid but unusual data is contrary to how most JS APIs work. So we tried to avoid that in Temporal. This is not a unique problem to Temporal; it represents a real-world problem that all software needs to deal with. If you buy a yearly subscription for a product on Feb 29, you don't get to skip paying the bill until the next leap year! If your automated email system sends an email every day at 2:30AM, it shouldn't skip the day that DST starts. And so on. Across a very wide range of these real-world use cases, what we observed is that these data-dependent edge cases are handled (by existing software) by defining default behaviors for how to resolve ambiguity automatically. For example: if a year doesn't have a leap day, then a result that would normally be Feb 29 is automatically constrained to Feb 28. If a time of 2:30 AM is requested on a day DST starts, then 3:30AM is used instead. Many of these defaults were inherited from existing APIs, including JS's own Date object.
As a shorthand among the Temporal champions, "no data-driven exceptions" means that data-dependent ambiguous cases should default to some reasonable behavior instead of throwing. In retrospect this isn't a good name! But what it means is that if you write your code and it doesn't throw for a normal date and time, then it also shouldn't throw BY DEFAULT for a "weird" date or time. I emphasize BY DEFAULT above because there are cases where developers do want to throw when confronted with weird data. If I want to send an email in the middle of the hour skipped by a DST transition, then it's fine to send that email later that day. But if I'm creating a baby's birth certificate and I for that time, I want to warn the user that the time is invalid! To handle these cases, Temporal always has a way to throw an exception, usually by using an option with the value reject. Note that most other APIs (like legacy Date) don't offer an option to throw. They just silently fix up the weird data and return the result. So "no data driven exceptions" isn't really a new thing, it's just how all other date/time APIs work. What's new is that Temporal provides "yes data driven exceptions!" options to support the unusual cases where ambiguity is not acceptable.
Temporal objects are immutable and always contain valid data. With the old JavaScript Date you can create a Date object that has NaN in its internal slot, representing an "invalid date". You can't do this with Temporal objects. In this normative change we are talking about converting from one type of Temporal object to another, so this is the kind of "valid data" we are talking about. For completeness, I'll go over the other kinds data that we consider valid.
Also note that obviously bad data (like a negative hour or zeroth month) will always throw. These are not "weird data", they're just plain invalid. However, values like { month: 13 } might be valid in a non-Gregorian calendar, so positive out-of-range day and month values don't throw by default either. The general principle is that if an input could be valid in some day, some month, some year, some calendar, etc. then we we don't throw by default, even if it's not present in a particular day/month/year/calendar. If we can determine _without_ doing any calendar calculation that any individual value is invalid, like a negative day, then the property bag is not valid data and is not subject to clamping. It's a messy principle (because dates and times and calendars and time zones are messy!) but it's consistent.
Finally, parsing strings must also adhere to the grammar defined in ISO 8601 and IXDTF (the new IETF RFC). If a string input doesn't adhere to those specs, we always throw to be compliant. The flexibility discussed above only applies to cases where we're interpreting number inputs, not string inputs. Here are some examples of what's valid and what's not. The top one is a valid month-day string. The next one is a valid date string. The third line has some month-day and date strings that are ISO 8601 and IXDTF do not include time zone transitions in their definition of validity. That'd be impossible. The string on the second-to-last line is a nonexistent time because it's in the middle of the hour when we set the clocks forward this spring in my time zone, but it's a valid string and when you convert it to a Temporal object, you can choose with an option whether to clamp it or throw. The string on the last line is still not valid because 99:99 is just not a time that clocks can display.
Here's an overview of the ways that you can convert data in Temporal that could be invalid in the result domain. What's not shown here is that most conversions can't fail at all (at least not due to being invalid in the result domain like February 29th 2030), and so don't need to be clamped. An example of a conversion that can't fail is converting from a PlainDateTime to a PlainDate. Every valid PlainDateTime object can be converted to a valid PlainDate. Most failable conversions clamp by default, but have an option to throw. On the left are examples of what circumstances this occurs in. (Sorry, it's a bit crammed in.) The ones that don't have an option to throw are because we considered them a convenience conversion, and so they use the default option. If you want the throwing behaviour, you can specify it manually by using `from()` or whatever. Given this context, let's come back to the normative change. In the current Temporal spec, the ones under this "BUG" heading don't follow the same default behavior as other Temporal APIs, which is to constrain the output to a valid date if the desired date doesn't exist when the receiver is combined with the input. Basically we want to empty the "Throw" category into the "Always clamp" category.
This is a spec bug that was discovered by a user, and we want to fix it so that the default behavior of all similar Temporal APIs are consistent. If we didn't make this change, then common use cases like "what day of the week is my birthday next year?" could throw, which would be quite unexpected. If this proposal were still stage 2, it'd probably be good to provide the option allowing you to choose the throwing behaviour. We don't want to do that at this time, but will track that as a possibility for a follow-on proposal.
Can we get consensus for this normative change?