Repository navigation
fix: relative dates roll over month ends like Git, instead of clamping - #2910
Merged
Sebastian Thiel (Byron) merged 4 commits intoAug 13, 2026
Conversation
…g. (GitoxideLabs#2909) `N months/years ago` now steps down the calendar fields while keeping the day-of-month, so a day beyond the end of the target month rolls over into the following month, the way `mktime(3)` normalizes it for Git: one month before 2026-03-31 now is 2026-03-03, no longer 2026-02-28. Count-unit pairs also apply in input order and accumulate when repeated, both matching Git as well. Assisted-by: Claude Co-authored-by: Cursor <[email protected]>
Sebastian Thiel (Byron)
force-pushed
the
fix/relative-date-month-rollover
branch
from
August 13, 2026 06:02
630e40e to
79da29c
Compare
<!-- agent --> - [P2] Guard month-total subtraction against overflow — gix-date/src/parse/relative.rs:170-170 When multiple pairs first move an ordinary current date into a BCE year and then supply a near-`i64::MAX` month count (for example, `2027 years 9223372036854775807 months ago` in 2026), this subtraction underflows and panics in debug builds instead of returning a parse error. Use checked arithmetic for the total calculation. Assisted-by: GPT 5.6 Co-authored-by: GPT 5.6 <[email protected]>
Sebastian Thiel (Byron)
force-pushed
the
fix/relative-date-month-rollover
branch
from
August 13, 2026 07:07
79da29c to
5488d5c
Compare
Sebastian Thiel (Byron)
force-pushed
the
fix/relative-date-month-rollover
branch
from
August 13, 2026 07:22
5488d5c to
fdac92f
Compare
Member
|
Thanks a lot! Please beware that there is no need to create an issue first, as this project believes that |
Sebastian Thiel (Byron)
enabled auto-merge
August 13, 2026 07:23
Sebastian Thiel (Byron)
disabled auto-merge
August 13, 2026 07:35
This allows time computation to be timezone aware, just like in Git. Assisted-by: GPT 5.6 Co-authored-by: GPT 5.6 <[email protected]>
Assisted-by: GPT 5.6 Co-authored-by: GPT 5.6 <[email protected]>
Sebastian Thiel (Byron)
force-pushed
the
fix/relative-date-month-rollover
branch
from
August 13, 2026 07:36
fdac92f to
613ff86
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2909.
gix_date::parse()used to merge all relative count-unit pairs into a singlejiff::Spanand subtract it with jiff's calendar arithmetic, which clamps to the end of the target month. Git'sapproxidate()instead steps downtm_mon/tm_year-style calendar fields while leaving the day-of-month alone, and letsmktime(3)roll a day beyond the end of the month over into the following month. Around month ends the two disagree by 1–3 days: one month before 2026-03-31 is 2026-03-03 to Git, not 2026-02-28.What changed
parse_ago()now returns the recognized<count> <unit>pairs in input order instead of one mergedSpan.subtract_pairs()applies them the way Git does:secondthroughweeksubtract a fixed number of seconds from the timestamp, whilemonthandyearonly step down the year/month fields of astruct tm-likeFieldsvalue whose day may temporarily point beyond the end of the month, and which normalizes likemktime(3)— rolling any excess days over into the following month. As in Git, each month/year pair first normalizes whatever the previous pairs left behind, pairs apply in input order, and repeated units accumulate.now,today,yesterday) are unaffected.various()test now uses a fixednow(the baseline'sGIT_TEST_DATE_NOW=1000000000): its expected values are computed with jiff's clamping subtraction and would disagree with Git — and with this fix — when the test happens to run near the end of a longer month.Couldn't parse span from '<unit> <count>', and the historical negative-span failure case is kept.Why not extend the baseline instead?
The baseline fixture pins
GIT_TEST_DATE_NOW=1000000000(2001-09-09) for all relative dates, which can never produce a month-end rollover. The newmonths_and_years_roll_over_month_ends_like_gittest therefore hard-codes values obtained from Git itself; each one reproduces withTZ=UTC GIT_TEST_DATE_NOW=<now> git rev-parse --since='<input>'(git 2.48.1), as noted in the test. I'm happy to extend the baseline format to support per-entrynowvalues instead, if that's preferred.Test evidence
The new test fails before the fix and passes after it:
Full crate test suite:
cargo clippy -p gix-date --all-targetsis clean. Cross-checked all twelve table rows from #2909 (plus order/accumulation cases) againstgit rev-parse --sincewith pinnedGIT_TEST_DATE_NOW: gix-date now matches Git exactly on every one.Disclosure: this PR was prepared with AI assistance (also marked with an
Assisted-by:commit trailer); I reviewed the change and verified every reproduction locally against git 2.48.1.