mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-02 02:11:58 +00:00
fix(usage): remove timezone date-window expansion in aggregated dashboard
This commit is contained in:
parent
5833d3eadd
commit
9783ef4e79
2 changed files with 40 additions and 30 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import asyncio
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime
|
||||
from types import SimpleNamespace
|
||||
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
|
||||
|
||||
|
|
@ -386,38 +386,20 @@ def _adjust_dates_for_timezone(
|
|||
timezone_offset_minutes: Optional[int],
|
||||
) -> Tuple[str, str]:
|
||||
"""
|
||||
Adjust date range to account for timezone differences.
|
||||
Return the date range unchanged.
|
||||
|
||||
The database stores dates in UTC. When a user in a different timezone
|
||||
selects a local date range, we need to expand the UTC query range to
|
||||
capture all records that fall within their local date range.
|
||||
``LiteLLM_DailyUserSpend.date`` is stored as a ``YYYY-MM-DD`` UTC string
|
||||
at day granularity, and the UI formats ``start_date``/``end_date`` from
|
||||
the browser's local year/month/day — so the strings already line up with
|
||||
how rows are keyed. The previous ±1 day expansion pulled an adjacent UTC
|
||||
day into the result set, inflating single-day totals for any non-UTC
|
||||
user (e.g. IST users saw two days summed together).
|
||||
|
||||
Args:
|
||||
start_date: Start date in YYYY-MM-DD format (user's local date)
|
||||
end_date: End date in YYYY-MM-DD format (user's local date)
|
||||
timezone_offset_minutes: Minutes behind UTC (positive = west of UTC)
|
||||
This matches JavaScript's Date.getTimezoneOffset() convention.
|
||||
For example: PST = +480 (8 hours * 60 = 480 minutes behind UTC)
|
||||
|
||||
Returns:
|
||||
Tuple of (adjusted_start_date, adjusted_end_date) in YYYY-MM-DD format
|
||||
``timezone_offset_minutes`` is kept in the signature for compatibility
|
||||
with callers; it is intentionally ignored.
|
||||
"""
|
||||
if timezone_offset_minutes is None or timezone_offset_minutes == 0:
|
||||
return start_date, end_date
|
||||
|
||||
start = datetime.strptime(start_date, "%Y-%m-%d")
|
||||
end = datetime.strptime(end_date, "%Y-%m-%d")
|
||||
|
||||
if timezone_offset_minutes > 0:
|
||||
# West of UTC (Americas): local evening extends into next UTC day
|
||||
# e.g., Feb 4 23:59 PST = Feb 5 07:59 UTC
|
||||
end = end + timedelta(days=1)
|
||||
else:
|
||||
# East of UTC (Asia/Europe): local morning starts in previous UTC day
|
||||
# e.g., Feb 4 00:00 IST = Feb 3 18:30 UTC
|
||||
start = start - timedelta(days=1)
|
||||
|
||||
return start.strftime("%Y-%m-%d"), end.strftime("%Y-%m-%d")
|
||||
del timezone_offset_minutes
|
||||
return start_date, end_date
|
||||
|
||||
|
||||
def _build_where_conditions(
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ sys.path.insert(
|
|||
) # Adds the parent directory to the system path
|
||||
|
||||
from litellm.proxy.management_endpoints.common_daily_activity import (
|
||||
_adjust_dates_for_timezone,
|
||||
_is_user_agent_tag,
|
||||
get_api_key_metadata,
|
||||
get_daily_activity,
|
||||
|
|
@ -57,6 +58,33 @@ async def test_get_daily_activity_empty_entity_id_list():
|
|||
assert where_conditions["team_id"] == {"in": []}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"timezone_offset_minutes",
|
||||
[
|
||||
pytest.param(480, id="pst_west_of_utc"),
|
||||
pytest.param(-330, id="ist_east_of_utc"),
|
||||
pytest.param(0, id="utc"),
|
||||
pytest.param(None, id="no_offset"),
|
||||
],
|
||||
)
|
||||
def test_adjust_dates_for_timezone_single_day_is_unchanged(timezone_offset_minutes):
|
||||
# Regression: selecting a single local day in any timezone must keep the
|
||||
# query window pinned to that one day. Previously, ±1 day was added based
|
||||
# on the JS timezone offset, which inflated single-day totals on the
|
||||
# aggregated usage dashboard for non-UTC users.
|
||||
start, end = _adjust_dates_for_timezone(
|
||||
"2026-04-15", "2026-04-15", timezone_offset_minutes
|
||||
)
|
||||
assert start == "2026-04-15"
|
||||
assert end == "2026-04-15"
|
||||
|
||||
|
||||
def test_adjust_dates_for_timezone_multi_day_range_is_unchanged():
|
||||
start, end = _adjust_dates_for_timezone("2026-04-10", "2026-04-15", -330)
|
||||
assert start == "2026-04-10"
|
||||
assert end == "2026-04-15"
|
||||
|
||||
|
||||
def test_is_user_agent_tag():
|
||||
"""Test _is_user_agent_tag function."""
|
||||
# Test None and empty string
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue