moment / luxon

⏱ A library for working with dates and times in JS
https://moment.github.io/luxon
MIT License
15.12k stars 728 forks source link

Regression: Duration.normalize() not working properly after 3.4.1 #1493

Closed thomassth closed 11 months ago

thomassth commented 11 months ago

Describe the bug See https://github.com/moment/luxon/actions/runs/5943754818/job/16119553727

- Expected  - 2
+ Received  + 2

  Object {
-   "days": 363,
-   "years": 1,
+   "days": -2,
+   "years": 2,
  }

- Expected  - 2
+ Received  + 2

  Object {
-   "days": 2,
-   "months": 0,
+   "days": -28,
+   "months": 1,
  }

Likely the fix for #1482 in 3.4.1 (https://github.com/moment/luxon/commit/11e454c0fdd2da8cc6b5821b89c03c52a7c64b91) broke #1467

To Reproduce Duration.fromObject({ hours: 96, minutes: 0, seconds: -10 }).normalize().toObject()) should equal { hours: 95, minutes: 59, seconds: 50} but no conversion happened.

Actual vs Expected behavior All tests should be passing

tonysamperi commented 11 months ago

Exactly, just wanted to report the same! I discovered it while updating my ts-luxon. I don't mean to be rude, but what's the point of having tests if you don't run them before publishing? Anyway I can try to figure out a way to make both

"Duration#normalize rebalances negative units" and the newer "Duration#shiftTo handles mixed units"

if you confirm that both can pass at the same time.

Cheers

image

thomassth commented 11 months ago

In a way we can just run rescale() before every normalize(), since rescale never had any problem on conversion

But that feels wrong

tonysamperi commented 11 months ago

@thomassth no we can't rescale, because rescale calls normalize 😅

diesieben07 commented 11 months ago

I apologize for the broken release again. I'm currently sitting down with pen and paper to revise the normalization algorithm once and for all. It has caused nothing but trouble in the past, resulting in numerous issues like this.

tonysamperi commented 11 months ago

@diesieben07 is there an analysis of all the use cases the algorythm should cover? Because I'm really intrigued by the problem...yesterday I spent several hours, but when I solved one, I broke another. Also I'm having trouble to understand if the problem are negative units, or adjustments that happen between distant units (e.g year / day) which have then a very small raw value...

diesieben07 commented 11 months ago

Here is (as far as I see it) what normalize should do (assuming the overall value of the duration is positive):

The previous implementations never really made this distinction clear and attempted to handle both cases in unison. I have now implemented two code paths, one for each case. All tests now pass, but I am going to investigate further if all cases are covered by the current test suite before committing to a pull request.

As for units which are overall negative in value, they are handled the same way, except that every step is inverted. For example { years: -2, days: 2 } becomes { years: -1, days: -363 }.

tonysamperi commented 11 months ago

Ok that makes sense. And I saw in the code the handling of overall negative values, managed with a double negate. That should be fine as long as the 2 cases above are handled correctly.

diesieben07 commented 11 months ago

And I saw in the code the handling of overall negative values, managed with a double negate.

Correct. Although I have improved this now, removing the need for multiple intermediate objects.