-
Notifications
You must be signed in to change notification settings - Fork 159
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
Should PlainYearMonth throw when adding days? #1731
Comments
You added 500 days and got a result that’s in the same year? |
Sorry, copy paste error. Fixed. |
A better example that shows clearly that PlainYearMonth is really just PlainDate with the day field set to 1: Temporal.PlainYearMonth.from('2019-06').add({days: 29}).toString()
// => '2019-06'
Temporal.PlainYearMonth.from('2019-06').add({days: 30}).toString()
// => '2019-07' |
This was previously discussed in #324 and in https://github.com/tc39/proposal-temporal/blob/main/meetings/agenda-minutes-2020-03-05.md. I agree this is a tradeoff. My opinion at the time was that it would be more surprising to users for such additions to throw, than it would be to assume midnight on the first of the month for the purposes of addition. |
Ignoring unknown fields would also be acceptable. For example, Temporal.PlainDate.from('2019-06-02').add({days: 1, foo: 1}) Throwing would be more a more obvious way to point out the more likely programmer error, but I think Perhaps the problem is in the definition: The current behavior goes against the conceptual definition of the types at hand. A |
Thanks @devongovett for raising this issue. I'll add a few notes. There are really two cases:
(1) seems like a lower-priority issue because the larger units can never affect the result. There's no ambiguity involved. So I think that ignoring the larger units is fine for this case. (2) is potentially problematic. Thankfully leap days & months are not a concern because PlainMonthDay doesn't have
In other parts of Temporal when there's possible ambiguity, we throw or provide options (e.g. I do feel strongly that we should not add additional options to My weakly-held opinion is that throwing makes sense here, and I'd support a normative change to throw. Specifically:
|
My preference in order of best to worst is:
My opinion on this hasn't changed since #324. Although, I will note that what has changed in the meantime is that we throw on |
One argument in favour of keeping the current behaviour is that it "just works" in a way that I believe would be the most common user expectation with negative durations, subtracting from the end of the month and adding to the beginning of the month: ym = Temporal.Now.plainDateISO().toPlainYearMonth() // 2021-08
ym.add({ days: 15 }) // 2021-08
ym.add({ days: -15 }) // 2021-08
ym.add({ days: 31 }) // 2021-09
ym.add({ days: -31 }) // 2021-07 This isn't obvious if you have to reimplement it for yourself in userland, and is a potential pitfall. |
The more I look at it, the more it really feels wrong to operate on a "year month" - a type in which days effectively do not exist - with "days". If you want to add days, you should convert it to a full date. |
Does anyone feel that we have learned new information that would change the consensus reached in #324? If not, I think we should close this. |
Yes, my previous comment indicates that i think throwing is more preferable. Users shouldn’t have a mental model that a YearMonth is just “Y-M-01”; it’s a distinct type. |
We had a previous discussion on this where we weighed all the alternatives in #324 and came to a tradeoff, and we haven't learned any information that invalidates any of the input that we used to determine that tradeoff, so we'll close this. |
@ptomato my comment contradicts that; can you address it? |
YearMonth indeed was overlooked in the original discussion, but it was eventually discussed in #418. The principle deemed most important for the resolution of #324 was Shane and Philipp's point about data-driven exceptions. I don't think anyone was overjoyed about introducing an inconsistency with YearMonth, but it doesn't change anything about the weight of YearMonth in the tradeoff. In fact, later we explicitly formulated a design principle about inconsistencies in YearMonth and MonthDay (#874) weighing less. |
Rereading those, I'm still not clear why we wouldn't want YearMonth to always throw when adding days. That wouldn't be a data-driven exception, it'd be a type-driven exception - an appropriate one. |
I find this confusing:
It seems to leak implementation details:
PlainYearMonth
is the same asPlainDate
but with theday
field set to1
by default. Conceptually,PlainYearMonth
is meant to represent the entire month, i.e any day within the month, but adding days assumes that it actually represents the first of the month.This is true of other fields and data types as well, e.g. you can add
hours
to aPlainDate
, which assumes it is midnight by default.I think it would make more sense to be more strict about what fields can be added to each type such that only the smallest field represented by the type is allowed to be added.
The text was updated successfully, but these errors were encountered: