Skip to content

Fix NaiveDateTime.diff/3 over-counting incomplete units#15583

Merged
josevalim merged 1 commit into
elixir-lang:mainfrom
nseaSeb:fix-naive-datetime-diff-incomplete-unit-overcount
Jul 10, 2026
Merged

Fix NaiveDateTime.diff/3 over-counting incomplete units#15583
josevalim merged 1 commit into
elixir-lang:mainfrom
nseaSeb:fix-naive-datetime-diff-incomplete-unit-overcount

Conversation

@nseaSeb

@nseaSeb nseaSeb commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

NaiveDateTime.diff/3 truncated each operand to the requested unit and only then subtracted, i.e. it computed floor(t1) - floor(t2) instead of floor(t1 - t2). When the subtrahend's sub-second fraction exceeded the minuend's, this rounded the result up, contradicting the documented guarantee that "Fractional results are [...] truncated" and the example that it "rounds incomplete days to zero".

For instance an elapsed span of 86399.5s (less than one day) returned 1 for :day, and a 0.5s span returned 1 for :second. The sibling DateTime.diff/3, which carries the identical doc line, returned 0 for the same wall-clock instants because it computes the difference at the :microsecond base first and truncates once via System.convert_time_unit/3.

Align NaiveDateTime.diff/3 with DateTime.diff/3 so both agree on identical instants. All existing doctests are preserved.

Assisted-by: Claude Code:claude-opus-4-8

`NaiveDateTime.diff/3` truncated each operand to the requested unit and
only then subtracted, i.e. it computed `floor(t1) - floor(t2)` instead of
`floor(t1 - t2)`. When the subtrahend's sub-second fraction exceeded the
minuend's, this rounded the result *up*, contradicting the documented
guarantee that "Fractional results are [...] truncated" and the example
that it "rounds incomplete days to zero".

For instance an elapsed span of 86399.5s (less than one day) returned `1`
for `:day`, and a 0.5s span returned `1` for `:second`. The sibling
`DateTime.diff/3`, which carries the identical doc line, returned `0` for
the same wall-clock instants because it computes the difference at the
`:microsecond` base first and truncates once via `System.convert_time_unit/3`.

Align `NaiveDateTime.diff/3` with `DateTime.diff/3` so both agree on
identical instants. All existing doctests are preserved.

Assisted-by: Claude Code:claude-opus-4-8
@josevalim josevalim merged commit 5cad659 into elixir-lang:main Jul 10, 2026
15 checks passed
@josevalim

Copy link
Copy Markdown
Member

💚 💙 💜 💛 ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants