Skip to content

Assertion failure and shady results in DifferenceZonedDateTime across backward UTC shift #3311

Description

@ptomato

Discovered thanks to Firefox fuzzing: https://bugzilla.mozilla.org/show_bug.cgi?id=2028872

Reproducer:

const zdt1 = Temporal.ZonedDateTime.from('1867-10-18T12:44:35.000000001-11:47[America/Adak]');
const zdt2 = Temporal.ZonedDateTime.from('1867-10-19T00:00:00+12:13[America/Adak]');
const realDuration = zdt1.since(zdt2);

zdt1.since(zdt2, { largestUnit: 'days' });
zdt2.until(zdt1, { largestUnit: 'days' });
realDuration.round({ largestUnit: 'days', relativeTo: zdt2 });
realDuration.total({ unit: 'days', relativeTo: zdt2 });

The four method calls (since, until, round, total) all hit the assertion in step 3 of CombineDateAndTimeDuration ("Assert: If dateSign ≠ 0 and timeSign ≠ 0, dateSign = timeSign.") via step 15 of DifferenceZonedDateTime ("Return CombineDateAndTimeDuration(dateDifference, timeDuration)"). The cause is pretty simple. For the input data above, zdt1 is chronologically later than zdt2 but earlier on the wall-clock, because of the backwards time zone shift. The day correction loop in DifferenceZonedDateTime handles that fine if the two times occur within the same calendar day, but here they don't and that's exactly the case that fails the assertion.

I'm not 100% sure what the results of the above calls should be, but my first guess would be that since, until, and round should all return a duration of equal length to realDuration. We should figure this out first, and then add test262 tests and adjust the assertion accordingly, since it is invalid.

In addition while investigating this, I also found shady results when rounding relative to the later ZonedDateTime (zdt1 instead of zdt2):

// Note, realDuration is PT12H44M35.000000001S
realDuration.round({ largestUnit: 'days', relativeTo: zdt1 });
  // PT36H44M35.000000001S
realDuration.total({ unit: 'days', relativeTo: zdt1 });
  // 0.5309606481481597

At first glance, the total result looks correct (it's the float approximation of (12 + 44/60 + 35/3600 + 1/3600e9) / 24), but the round result looks completely wrong and should probably be equal to realDuration. I still need to double-check these results and find where the bug (if any) is.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions