From 7dbebaa1830e7f91ca30e6fc139576a16426a964 Mon Sep 17 00:00:00 2001 From: nvikou Date: Fri, 4 Sep 2026 19:47:10 +0100 Subject: [PATCH 1/2] fix: remove dead conditional branches in duration reset handlers _handle_hour_reset, _handle_minute_reset and _handle_second_reset each branched on whether the sub-unit fields were exactly zero, but both branches computed an identical expression. The conditions suggested that the exact-boundary case was handled specially when it was not, which is misleading for anyone maintaining budget reset logic. Collapse each branch to the single expression and drop the locals that become unused as a result. No behaviour change: verified by differential testing across 1,733,472 combinations of duration, timezone, reset_time_of_day and timestamp - including exact unit boundaries, invalid durations, leap day and year end - with zero mismatches. --- litellm/litellm_core_utils/duration_parser.py | 33 ++++--------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/litellm/litellm_core_utils/duration_parser.py b/litellm/litellm_core_utils/duration_parser.py index 7ac2299b7d8..3e5b1d3ddf2 100644 --- a/litellm/litellm_core_utils/duration_parser.py +++ b/litellm/litellm_core_utils/duration_parser.py @@ -260,15 +260,9 @@ def _handle_hour_reset(current_time: datetime, base_midnight: datetime, value: i return current_time current_hour: Final = current_time.hour - current_minute: Final = current_time.minute - current_second: Final = current_time.second - current_microsecond: Final = current_time.microsecond # Calculate next hour aligned with the value - if current_minute == 0 and current_second == 0 and current_microsecond == 0: - next_hour = current_hour + value - (current_hour % value) if current_hour % value != 0 else current_hour + value - else: - next_hour = current_hour + value - (current_hour % value) if current_hour % value != 0 else current_hour + value + next_hour = current_hour + value - (current_hour % value) if current_hour % value != 0 else current_hour + value # Handle overnight case if next_hour >= 24: @@ -287,18 +281,11 @@ def _handle_minute_reset(current_time: datetime, base_midnight: datetime, value: current_hour: Final = current_time.hour current_minute: Final = current_time.minute - current_second: Final = current_time.second - current_microsecond: Final = current_time.microsecond # Calculate next minute aligned with the value - if current_second == 0 and current_microsecond == 0: - next_minute = ( - current_minute + value - (current_minute % value) if current_minute % value != 0 else current_minute + value - ) - else: - next_minute = ( - current_minute + value - (current_minute % value) if current_minute % value != 0 else current_minute + value - ) + next_minute = ( + current_minute + value - (current_minute % value) if current_minute % value != 0 else current_minute + value + ) # Handle hour rollover next_hour = current_hour + (next_minute // 60) @@ -322,17 +309,11 @@ def _handle_second_reset(current_time: datetime, base_midnight: datetime, value: current_hour: Final = current_time.hour current_minute: Final = current_time.minute current_second: Final = current_time.second - current_microsecond: Final = current_time.microsecond # Calculate next second aligned with the value - if current_microsecond == 0: - next_second = ( - current_second + value - (current_second % value) if current_second % value != 0 else current_second + value - ) - else: - next_second = ( - current_second + value - (current_second % value) if current_second % value != 0 else current_second + value - ) + next_second = ( + current_second + value - (current_second % value) if current_second % value != 0 else current_second + value + ) # Handle minute rollover additional_minutes: Final = next_second // 60 From 798f86b58ca84a6bb1f825b9964168fe61c4face Mon Sep 17 00:00:00 2001 From: nvikou Date: Fri, 4 Sep 2026 19:47:10 +0100 Subject: [PATCH 2/2] test: pin reset alignment against sub-unit precision Adds cases asserting that hour, minute and second resets depend only on the unit they align on, so the removed branches cannot be reintroduced without a failing test. --- .../test_duration_parser.py | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/test_litellm/litellm_core_utils/test_duration_parser.py b/tests/test_litellm/litellm_core_utils/test_duration_parser.py index cb9f273a0a7..9a0a0852efc 100644 --- a/tests/test_litellm/litellm_core_utils/test_duration_parser.py +++ b/tests/test_litellm/litellm_core_utils/test_duration_parser.py @@ -320,6 +320,59 @@ class TestResetTimeOfDay(unittest.TestCase): datetime(2023, 5, 16, 0, 0, 0, tzinfo=timezone.utc), ) + def test_boundary_alignment_is_independent_of_sub_unit_precision(self): + """Reset alignment must depend only on the unit being aligned on. + + _handle_hour_reset, _handle_minute_reset and _handle_second_reset each + used to branch on whether the sub-unit fields were exactly zero, but + both branches computed the same value. These cases pin that the + sub-unit fields genuinely do not affect the result, so the dead + branches cannot be reintroduced silently. + """ + # --- hours: minutes/seconds/microseconds must not shift the result --- + for minute, second, micro in [ + (0, 0, 0), + (0, 0, 1), + (30, 15, 500000), + (59, 59, 999999), + ]: + t = datetime( + 2023, 5, 15, 14, minute, second, micro, tzinfo=timezone.utc + ) + self.assertEqual( + get_next_standardized_reset_time("2h", t, "UTC"), + datetime(2023, 5, 15, 16, 0, 0, tzinfo=timezone.utc), + f"2h reset shifted for minute={minute} " + f"second={second} micro={micro}", + ) + + # --- minutes: seconds/microseconds must not shift the result --- + for second, micro in [ + (0, 0), + (0, 1), + (30, 500000), + (59, 999999), + ]: + t = datetime( + 2023, 5, 15, 14, 30, second, micro, tzinfo=timezone.utc + ) + self.assertEqual( + get_next_standardized_reset_time("30m", t, "UTC"), + datetime(2023, 5, 15, 15, 0, 0, tzinfo=timezone.utc), + f"30m reset shifted for second={second} micro={micro}", + ) + + # --- seconds: microseconds must not shift the result --- + for micro in [0, 1, 500000, 999999]: + t = datetime( + 2023, 5, 15, 14, 30, 45, micro, tzinfo=timezone.utc + ) + self.assertEqual( + get_next_standardized_reset_time("15s", t, "UTC"), + datetime(2023, 5, 15, 14, 31, 0, tzinfo=timezone.utc), + f"15s reset shifted for micro={micro}", + ) + class TestWordFormBudgetDurations(unittest.TestCase): """The Admin UI historically persisted word-form budget durations