Skip to content

Preserve sub-day seconds for precisedelta month and year minimum units - #436

Open
PDGGK wants to merge 1 commit into
python-humanize:mainfrom
PDGGK:fix-precisedelta-coarse-unit-seconds
Open

PDGGK wants to merge 1 commit into
python-humanize:mainfrom
PDGGK:fix-precisedelta-coarse-unit-seconds

Conversation

@PDGGK

@PDGGK PDGGK commented Oct 3, 2026

Copy link
Copy Markdown

Changes proposed in this pull request:

  • Preserve sub-day elapsed seconds when rounding precisedelta to a minimum unit of months or years.
  • Divide the whole elapsed-second count directly by the scaled month/year divisor, then consume the lower components. This avoids both dropped seconds and intermediate double rounding.
  • Add regressions for fractional units, short durations, and rounding midpoints. Keep the existing 30.5-day month, 365-day year, negative-duration behavior, and documented microsecond-precision limitation.

Before and after

from datetime import timedelta
from humanize import precisedelta

precisedelta(timedelta(days=15, hours=6), minimum_unit="months", format="%.2f")
# Before: '0.49 months'
# After:  '0.50 months'

precisedelta(timedelta(days=30, hours=12), minimum_unit="months", format="%.2f")
# Before: '0.98 months'
# After:  '1 month'

precisedelta(timedelta(days=182, hours=12), minimum_unit="years", format="%.4f")
# Before: '0.4986 years'
# After:  '0.5000 years'

precisedelta(timedelta(hours=12), minimum_unit="years", format="%.2f")
# Before: '0 years and 43200000000 microseconds'
# After:  '0 years'

Validation

Linux, CPython 3.12.14:

  • Final full suite and source doctests: 883 passed, 2 translation-update tests skipped because GNU xgettext is unavailable.
  • Final focused selection: 63 passed; original production and production-only revert each reproduce the same 13 failures with 50 passing controls.
  • humanize.time statement coverage: 100%.
  • Unchanged configured prek hooks, Ruff, Black, strict documentation build, and serial mypy pass. Default multi-worker mypy is blocked by sandbox IPC sockets.

No Windows/macOS/other-Python matrix or hosted-CI result is claimed. Benchmark functions execute with timing disabled, so this is not a performance measurement.

AI assistance: implementation, tests, and this description were prepared with OpenAI assistance.

@Wkhanex Wkhanex left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new month/year branches include seconds but still discard delta.microseconds before formatting. At higher requested precision, a fractional remainder is therefore lost:

delta = timedelta(days=30.5, microseconds=999999)
precisedelta(delta, minimum_unit="months", format="%.12f")
# actual: "1 month"
# numeric duration / (30.5 * 86400): 1.000000379477

The analogous 365-day year case renders “1 year” instead of retaining the numeric 1.000000031710 remainder. Half-day extra-seconds controls correctly render 1.016393 months and 1.001370 years at six decimals. I used Python 3.12.14 against commit 9e3f2fc.

Could both new numerators include usecs / 1e6 before setting days/secs/usecs to zero, with high-precision regressions? This is a remaining sub-day precision case, not a claim about default two-decimal output. I used English-locale standalone checks against fetched PR source with only build-generated version metadata stubbed, not the full pytest suite.

@PDGGK

PDGGK commented Oct 13, 2026

Copy link
Copy Markdown
Author

Thanks for checking. The other coarse units also drop microseconds when they are the minimum unit: on main, precisedelta(timedelta(minutes=1, microseconds=999999), minimum_unit="minutes", format="%.12f") gives "1 minute", and likewise for hours and days. This PR brings months and years in line with those units by including the sub-day seconds; carrying microseconds through every coarse unit would be a separate change.

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.

2 participants