Skip to content

Use calendar months for humanize month and quarter granularity - #1391

Open
syahra712 wants to merge 1 commit into
arrow-py:masterfrom
syahra712:fix-humanize-month-granularity
Open

syahra712 wants to merge 1 commit into
arrow-py:masterfrom
syahra712:fix-humanize-month-granularity

Conversation

@syahra712

Copy link
Copy Markdown

Pull Request Checklist

  • 🧪 Added tests for changed code.
  • 🛠️ All tests pass when run locally (run tox or make test to find out!).
  • 🧹 All linting checks pass when run locally (run tox -e lint or make lint to find out!).
  • 📚 Updated documentation for changed code. (n/a, bug fix)
  • ⏩ Code is up-to-date with the master branch.

Description of Changes

With a single granularity="month" or "quarter", humanize divided the elapsed seconds by a fixed 30.5-day month (91.5-day quarter). Whole calendar months were then under-counted:

>>> a = arrow.Arrow(2023, 1, 1)
>>> a.shift(months=2).humanize(a, granularity="month")
'in a month'        # 59 days / 30.5
>>> a.shift(months=6).humanize(a, granularity="month")
'in 5 months'
>>> a.shift(months=3).humanize(a, granularity="quarter")
'in 0 quarters'

The "auto" granularity already counts calendar months with relativedelta. This change does the same for the single month and quarter granularities. The partial month is added as a fraction of the next calendar month, so it stays below 1 and keeps the "ago"/"in" direction when the count is 0 (e.g. Jan 1 → Jan 31 23:59 is still "0 months ago").

Multi-granularity lists are not changed here. #1367 handles those separately.

Tests

  • New test_granularity_calendar_months checks 1/2/6 months and 3/6/18-month quarters from Jan 31 (with month-end clamping), plus the "0 months" case for a span longer than 30.5 days. It fails without the fix.
  • One existing assertion changes: later506 is 2013-01-01 → 2014-08-02 16:53, which is 19 calendar months, not 18.
  • Full suite passes: 1903 tests, coverage 99.93%. pre-commit (black, flake8, isort, mypy) is clean on the changed files.

AI-assisted (Claude): I used it to help read the code and draft the test. I reviewed and ran everything myself.

granularity="month" and "quarter" divided the elapsed seconds by a
fixed 30.5-day month, so whole calendar months were under-counted:
Jan 1 -> Mar 1 was "in a month" and exactly 3 months was "in 0 quarters".
Count calendar months with relativedelta, as the auto granularity does.
@codecov

codecov Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (2224255) to head (4cd57c1).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff            @@
##            master     #1391   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           10        10           
  Lines         2315      2321    +6     
  Branches       358       358           
=========================================
+ Hits          2315      2321    +6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant