diff --git a/src/humanize/lists.py b/src/humanize/lists.py index 525f0e3..41aec9b 100644 --- a/src/humanize/lists.py +++ b/src/humanize/lists.py @@ -32,7 +32,6 @@ def natural_list(items: list[Any]) -> str: return "" if len(items) == 1: return str(items[0]) - elif len(items) == 2: - return f"{str(items[0])} and {str(items[1])}" - else: - return ", ".join([str(item) for item in items[:-1]]) + f" and {str(items[-1])}" + if len(items) == 2: + return f"{items[0]!s} and {items[1]!s}" + return ", ".join([str(item) for item in items[:-1]]) + f" and {items[-1]!s}" diff --git a/src/humanize/number.py b/src/humanize/number.py index 2fb22c6..c9b8587 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -540,19 +540,19 @@ def metric(value: float, unit: str = "", precision: int = 3) -> str: if not math.isfinite(value): return _format_not_finite(value) - exponent = int(math.floor(math.log10(abs(value)))) if value != 0 else 0 + exponent = math.floor(math.log10(abs(value))) if value != 0 else 0 if exponent >= 33 or exponent < -30: return scientific(value, precision - 1) + unit old_bucket = exponent // 3 * 3 value /= 10**old_bucket - digits = int(max(0, precision - exponent % 3 - 1)) + digits = max(0, precision - exponent % 3 - 1) if exponent < 30 and round(abs(value), digits) >= 1000: exponent += 3 - exponent % 3 new_bucket = exponent // 3 * 3 value /= 10 ** (new_bucket - old_bucket) - digits = int(max(0, precision - exponent % 3 - 1)) + digits = max(0, precision - exponent % 3 - 1) if exponent >= 3: ordinal_ = "kMGTPEZYRQ"[exponent // 3 - 1] diff --git a/src/humanize/time.py b/src/humanize/time.py index 4a07d52..17a9fa2 100644 --- a/src/humanize/time.py +++ b/src/humanize/time.py @@ -195,7 +195,7 @@ def naturaldelta( return _ngettext("%d minute", "%d minutes", minutes) % minutes - if 3600 <= delta.seconds: + if delta.seconds >= 3600: hours = round(delta.seconds / 3600) if hours == 1: return _("an hour") @@ -313,6 +313,15 @@ def _convert_aware_datetime( return value +def _today_for_value(value: dt.date | dt.datetime) -> dt.date: + """Return today's date in the timezone of a tz-aware datetime value.""" + import datetime as dt + + if isinstance(value, dt.datetime) and value.tzinfo is not None: + return dt.datetime.now(value.tzinfo).date() + return dt.date.today() + + def naturalday(value: dt.date | dt.datetime, format: str = "%b %d") -> str: """Return a natural day. @@ -326,10 +335,7 @@ def naturalday(value: dt.date | dt.datetime, format: str = "%b %d") -> str: try: # When value is a tz-aware datetime, compute "today" in that timezone # so the comparison uses the correct local date. - if isinstance(value, dt.datetime) and value.tzinfo is not None: - today = dt.datetime.now(value.tzinfo).date() - else: - today = dt.date.today() + today = _today_for_value(value) value = dt.date(value.year, value.month, value.day) except AttributeError: # Passed value wasn't date-ish @@ -357,10 +363,7 @@ def naturaldate(value: dt.date | dt.datetime) -> str: original_value = value try: - if isinstance(value, dt.datetime) and value.tzinfo is not None: - today = dt.datetime.now(value.tzinfo).date() - else: - today = dt.date.today() + today = _today_for_value(value) value = dt.date(value.year, value.month, value.day) except AttributeError: # Passed value wasn't date-ish @@ -643,7 +646,7 @@ def precisedelta( import math texts: list[str] = [] - for unit, fmt in zip(reversed(Unit), fmts): + for unit, fmt in zip(reversed(Unit), fmts, strict=True): singular_txt, plural_txt, fmt_value = fmt if fmt_value > 0 or (not texts and unit == min_unit): _fmt_value = 2 if 1 < fmt_value < 2 else int(fmt_value) diff --git a/tests/test_time.py b/tests/test_time.py index 7699770..70e4798 100644 --- a/tests/test_time.py +++ b/tests/test_time.py @@ -27,7 +27,7 @@ with freeze_time(FROZEN_DATE): NOW = dt.datetime.now() - NOW_UTC = dt.datetime.now(tz=dt.timezone.utc) + NOW_UTC = dt.datetime.now(tz=dt.UTC) NOW_UTC_PLUS_01_00 = dt.datetime.now(tz=dt.timezone(offset=dt.timedelta(hours=1))) TODAY = dt.date.today() TOMORROW = TODAY + ONE_DAY_DELTA @@ -66,7 +66,7 @@ def test_date_and_delta() -> None: td_tests = [td(seconds=x) for x in int_tests] results = [(now - td(seconds=x), td(seconds=x)) for x in int_tests] for t in (int_tests, date_tests, td_tests): - for arg, result in zip(t, results): + for arg, result in zip(t, results, strict=True): date, d = time._date_and_delta(arg) assert_equal_datetime(date, result[0]) assert_equal_timedelta(d, result[1]) @@ -303,7 +303,7 @@ def test_naturaldate(test_input: dt.date, expected: str) -> None: @freeze_time("2023-10-15 23:00:00+00:00") def test_naturaldate_tz_aware() -> None: """naturaldate should compare dates in the timezone of the given value.""" - utc = dt.timezone.utc + utc = dt.UTC aedt = dt.timezone(dt.timedelta(hours=11)) cest = dt.timezone(dt.timedelta(hours=2)) edt = dt.timezone(dt.timedelta(hours=-4))