@@ -347,79 +347,41 @@ def _quotient_and_remainder(
347
347
unit : Unit ,
348
348
minimum_unit : Unit ,
349
349
suppress : Iterable [Unit ],
350
+ format : str ,
350
351
) -> tuple [float , float ]:
351
352
"""Divide `value` by `divisor`, returning the quotient and remainder.
352
353
353
- If `unit` is `minimum_unit`, the quotient will be a float number and the remainder
354
- will be zero. The rationale is that if `unit` is the unit of the quotient, we cannot
355
- represent the remainder because it would require a unit smaller than the
356
- `minimum_unit`.
354
+ If `unit` is `minimum_unit`, the quotient will be the rounding of `value / divisor`
355
+ according to the `format` string and the remainder will be zero. The rationale is
356
+ that if `unit` is the unit of the quotient, we cannot represent the remainder
357
+ because it would require a unit smaller than the `minimum_unit`.
357
358
358
359
>>> from humanize.time import _quotient_and_remainder, Unit
359
- >>> _quotient_and_remainder(36, 24, Unit.DAYS, Unit.DAYS, [])
360
+ >>> _quotient_and_remainder(36, 24, Unit.DAYS, Unit.DAYS, [], "%0.2f" )
360
361
(1.5, 0)
361
362
362
363
If `unit` is in `suppress`, the quotient will be zero and the remainder will be the
363
364
initial value. The idea is that if we cannot use `unit`, we are forced to use a
364
365
lower unit, so we cannot do the division.
365
366
366
- >>> _quotient_and_remainder(36, 24, Unit.DAYS, Unit.HOURS, [Unit.DAYS])
367
+ >>> _quotient_and_remainder(36, 24, Unit.DAYS, Unit.HOURS, [Unit.DAYS], "%0.2f" )
367
368
(0, 36)
368
369
369
370
In other cases, return the quotient and remainder as `divmod` would do it.
370
371
371
- >>> _quotient_and_remainder(36, 24, Unit.DAYS, Unit.HOURS, [])
372
+ >>> _quotient_and_remainder(36, 24, Unit.DAYS, Unit.HOURS, [], "%0.2f" )
372
373
(1, 12)
373
374
374
375
"""
375
376
if unit == minimum_unit :
376
- return value / divisor , 0
377
+ return _rounding_by_fmt ( format , value / divisor ) , 0
377
378
378
379
if unit in suppress :
379
380
return 0 , value
380
381
381
382
return divmod (value , divisor )
382
383
383
384
384
- def _carry (
385
- value1 : float ,
386
- value2 : float ,
387
- ratio : float ,
388
- unit : Unit ,
389
- min_unit : Unit ,
390
- suppress : Iterable [Unit ],
391
- ) -> tuple [float , float ]:
392
- """Return a tuple with two values.
393
-
394
- If `unit` is in `suppress`, multiply `value1` by `ratio` and add it to `value2`
395
- (carry to right). The idea is that if we cannot represent `value1`, we need to
396
- represent it in a lower unit.
397
-
398
- >>> from humanize.time import _carry, Unit
399
- >>> _carry(2, 6, 24, Unit.DAYS, Unit.SECONDS, [Unit.DAYS])
400
- (0, 54)
401
-
402
- If `unit` is the minimum unit, divide `value2` by `ratio` and add it to `value1`
403
- (carry to left). We assume that `value2` has a lower unit, so we need to
404
- carry it to `value1`.
405
-
406
- >>> _carry(2, 6, 24, Unit.DAYS, Unit.DAYS, [])
407
- (2.25, 0)
408
-
409
- Otherwise, just return the same input:
410
-
411
- >>> _carry(2, 6, 24, Unit.DAYS, Unit.SECONDS, [])
412
- (2, 6)
413
- """
414
- if unit == min_unit :
415
- return value1 + value2 / ratio , 0
416
-
417
- if unit in suppress :
418
- return 0 , value2 + value1 * ratio
419
-
420
- return value1 , value2
421
-
422
-
423
385
def _suitable_minimum_unit (min_unit : Unit , suppress : Iterable [Unit ]) -> Unit :
424
386
"""Return a minimum unit suitable that is not suppressed.
425
387
@@ -574,23 +536,54 @@ def precisedelta(
574
536
# years, days = divmod(years, days)
575
537
#
576
538
# The same applies for months, hours, minutes and milliseconds below
577
- years , days = _quotient_and_remainder (days , 365 , YEARS , min_unit , suppress_set )
578
- months , days = _quotient_and_remainder (days , 30.5 , MONTHS , min_unit , suppress_set )
539
+ years , days = _quotient_and_remainder (
540
+ days , 365 , YEARS , min_unit , suppress_set , format
541
+ )
542
+ months , days = _quotient_and_remainder (
543
+ days , 30.5 , MONTHS , min_unit , suppress_set , format
544
+ )
579
545
580
546
secs = days * 24 * 3600 + secs
581
- days , secs = _quotient_and_remainder (secs , 24 * 3600 , DAYS , min_unit , suppress_set )
547
+ days , secs = _quotient_and_remainder (
548
+ secs , 24 * 3600 , DAYS , min_unit , suppress_set , format
549
+ )
582
550
583
- hours , secs = _quotient_and_remainder (secs , 3600 , HOURS , min_unit , suppress_set )
584
- minutes , secs = _quotient_and_remainder (secs , 60 , MINUTES , min_unit , suppress_set )
551
+ hours , secs = _quotient_and_remainder (
552
+ secs , 3600 , HOURS , min_unit , suppress_set , format
553
+ )
554
+ minutes , secs = _quotient_and_remainder (
555
+ secs , 60 , MINUTES , min_unit , suppress_set , format
556
+ )
585
557
586
- secs , usecs = _carry (secs , usecs , 1e6 , SECONDS , min_unit , suppress_set )
558
+ usecs = secs * 1e6 + usecs
559
+ secs , usecs = _quotient_and_remainder (
560
+ usecs , 1e6 , SECONDS , min_unit , suppress_set , format
561
+ )
587
562
588
563
msecs , usecs = _quotient_and_remainder (
589
- usecs , 1000 , MILLISECONDS , min_unit , suppress_set
564
+ usecs , 1000 , MILLISECONDS , min_unit , suppress_set , format
590
565
)
591
566
592
- # if _unused != 0 we have lost some precision
593
- usecs , _unused = _carry (usecs , 0 , 1 , MICROSECONDS , min_unit , suppress_set )
567
+ # Due to rounding, it could be that a unit is high enough to be promoted to a higher
568
+ # unit. Example: 59.9 minutes was rounded to 60 minutes, and thus it should become 0
569
+ # minutes and one hour more.
570
+ if msecs >= 1_000 and SECONDS not in suppress_set :
571
+ msecs -= 1_000
572
+ secs += 1
573
+ if secs >= 60 and MINUTES not in suppress_set :
574
+ secs -= 60
575
+ minutes += 1
576
+ if minutes >= 60 and HOURS not in suppress_set :
577
+ minutes -= 60
578
+ hours += 1
579
+ if hours >= 24 and DAYS not in suppress_set :
580
+ hours -= 24
581
+ days += 1
582
+ # When adjusting we should not deal anymore with fractional days as all rounding has
583
+ # been already made. We promote 31 days to an extra month.
584
+ if days >= 31 and MONTHS not in suppress_set :
585
+ days -= 31
586
+ months += 1
594
587
595
588
fmts = [
596
589
("%d year" , "%d years" , years ),
@@ -606,10 +599,6 @@ def precisedelta(
606
599
texts : list [str ] = []
607
600
for unit , fmt in zip (reversed (Unit ), fmts ):
608
601
singular_txt , plural_txt , fmt_value = fmt
609
-
610
- if unit == min_unit :
611
- fmt_value = _rounding_by_fmt (format , fmt_value )
612
-
613
602
if fmt_value > 0 or (not texts and unit == min_unit ):
614
603
_fmt_value = 2 if 1 < fmt_value < 2 else int (fmt_value )
615
604
fmt_txt = _ngettext (singular_txt , plural_txt , _fmt_value )
0 commit comments