top | item 28067374

(no title)

jimminy | 4 years ago

Plus/Increment/Add 1 month, does have one single meaning, it's that the outcome may be invalid that is the issue.

"2021-01-31".plus(unit="Month", size=1) => "2021-02-31"

But nobody really wants that, because it's not a valid date. So implicitly the library is deciding to return a valid date.

A library could be written to just provide invalid dates, and let the end user handle any errors. That library could also include an explicit validation method that takes a date and returns a valid one.

"2021-02-31".coerceToValid() => "2021-02-28" // Overflow == Max

"2021-02-31".coerceToValid(asDays=True) => "2021-03-03" // Overflow Carries (to the right)

In fact the library, that provides an ignorant response and no contract on validity would hold to the associative property, it just wouldn't be as ergonomic.

discuss

order

saurik|4 years ago

FWIW, I believe there are only two ways to build this library correctly: the way you described here (where intermediate date values are allowed to be invalid) and simply making "one month after 2021-01-31" throw an exception.

jimminy|4 years ago

I agree those are the technically correct way to do it.

But if the reason people are wanting to use the library, is they want something to handle the complexity for them, coercing the data is good for simplication.

There is actually another option, that provides idempotent/associative consistency and implicit coercion to valid values.

This option, which discards some use cases (days beyond 28, when manipulating months), you coerce all values 29..31 to 28. This isn't even as technically correct, as the original option, but it removes the inconsistency and holds to the simplification contract to users.

da_chicken|4 years ago

> Plus/Increment/Add 1 month, does have one single meaning

No, it doesn't.

You walk in to the doctor's office on February 28. At the desk, you see another patient about to leave. They turn to the desk attendant and say, "I'll see you in a month for my follow-up."

What date is the other patient's next appointment? What if the date you walked in had been January 31?

Also, for what it's worth, in C#:

  DateTime x = new DateTime(2021, 1, 31);
  x.AddMonths(1); // Feb 28
  x.AddMonths(2); // March 31
  x.AddMonths(1).AddMonths(1); // March 28

jimminy|4 years ago

I explained it pretty clearly what you get when you add a month on January 31st. You get February 31st.

A month is a discrete unit of measure. It is not decomposable into any number of days.

When you increment a month, you get YYYY - (MM+1). Any higher significance is maintained, but irrelevant to the operation. (This applies to the hypothetical statement in the doctor's office, the specific day is indeterminant, but can be assumed the same as current day next month.)

The fact that not all possible days exist is orthogonal to the singular meaning of the operation. It's obviously not greatly valuable to an end-user, but the method of addressing the ambiguity involves a second operation that ensures validity.

End-users want an method that does both the addition and coercion, but you can create consistency if you follow the simple path I laid out in GP.

I'll use your syntax but with the strictly correct definition of the operation.

  Datetime x = new DateTime(2021, 1, 31);
  x.AddMonths(1); // DateTime(2021, 2, 31)
  x.AddMonths(2); // DateTime(2021, 3, 31)
  x.AddMonths(1).AddMonths(1); //DateTime(2021, 3, 31)

  // Ensure Valid, using a coerce to clamp overflows
  DateTime(2021, 2, 31).EnsureValid(); // Feb 28
  DateTime(2021, 3, 31).EnsureValid(); // Mar 31
  DateTime(2021, 4, 31).EnsureValid(); // Apr 30