Skip to content

fix(ui): adjust daily spend date filtering for user timezone#20472

Merged
yuneng-jiang merged 1 commit intoBerriAI:mainfrom
nina-hu:fix/timezone-daily-spend-filtering
Feb 5, 2026
Merged

fix(ui): adjust daily spend date filtering for user timezone#20472
yuneng-jiang merged 1 commit intoBerriAI:mainfrom
nina-hu:fix/timezone-daily-spend-filtering

Conversation

@nina-hu
Copy link
Contributor

@nina-hu nina-hu commented Feb 5, 2026

Summary

The daily spend tables store dates in UTC, but the UI sends dates in the user's local timezone. This causes a mismatch where records from the user's evening (stored as the next UTC day) don't appear when filtering by "today".

Changes

  • Add _adjust_dates_for_timezone() helper to expand date range based on timezone offset
  • Add timezone query parameter to /user/daily/activity and /user/daily/activity/aggregated endpoints
  • Frontend sends timezone using Date.getTimezoneOffset()

How it works

  • For users west of UTC (e.g., PST with offset +480), end_date is extended by 1 day
  • For users east of UTC (e.g., IST with offset -330), start_date is extended by 1 day earlier
  • This ensures all records within the user's local date range are captured

Example

User in PST selects Feb 4:

  • Frontend sends: start_date=2026-02-04&end_date=2026-02-04&timezone=480
  • Backend adjusts query to: date >= '2026-02-04' AND date <= '2026-02-05'
  • Records from evening PST (stored as Feb 5 UTC) are now included

The daily spend tables store dates in UTC, but the UI sends dates in the
user's local timezone. This causes a mismatch where records from the
user's evening (stored as the next UTC day) don't appear when filtering
by "today".

Changes:
- Add `_adjust_dates_for_timezone()` helper to expand date range based
  on timezone offset
- Add `timezone` query parameter to `/user/daily/activity` and
  `/user/daily/activity/aggregated` endpoints
- Frontend sends `timezone` using `Date.getTimezoneOffset()`

For users west of UTC (e.g., PST), end_date is extended by 1 day.
For users east of UTC (e.g., IST), start_date is extended by 1 day earlier.
This ensures all records within the user's local date range are captured.
@vercel
Copy link

vercel bot commented Feb 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Feb 5, 2026 5:57am

Request Review

@CLAassistant
Copy link

CLAassistant commented Feb 5, 2026

CLA assistant check
All committers have signed the CLA.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 5, 2026

Greptile Overview

Greptile Summary

Fixes timezone mismatch between frontend date selection and backend UTC date filtering by expanding query date ranges based on user's timezone offset.

  • Added _adjust_dates_for_timezone() helper that expands date ranges by 1 day based on timezone direction (west of UTC extends end date, east of UTC extends start date)
  • Frontend now sends timezone offset using JavaScript's Date.getTimezoneOffset()
  • Backend endpoints accept optional timezone query parameter and pass it through the query chain
  • Logic correctly handles the sign convention where positive offsets = west of UTC, negative = east of UTC

The implementation is straightforward and solves the stated problem. No new database objects are created in the request path, satisfying the performance guidelines.

Confidence Score: 4/5

  • This PR is safe to merge with minor style improvements recommended
  • The timezone adjustment logic is mathematically correct and the implementation follows existing patterns. The changes are isolated, well-documented, and don't introduce database objects in the request path. Score is 4 instead of 5 due to missing input validation bounds on the timezone parameter
  • No files require special attention - all changes are straightforward

Important Files Changed

Filename Overview
litellm/proxy/management_endpoints/common_daily_activity.py Added _adjust_dates_for_timezone() helper function to expand date ranges based on timezone offset, ensuring UTC database queries capture all records within user's local date range
litellm/proxy/management_endpoints/internal_user_endpoints.py Added optional timezone query parameter to daily activity endpoints and passed through to common activity functions
ui/litellm-dashboard/src/components/networking.tsx Added timezone offset parameter using Date.getTimezoneOffset() to daily activity API calls, but offset is calculated at request time which may not match the date selection time

Sequence Diagram

sequenceDiagram
    participant UI as Frontend (networking.tsx)
    participant API as API Endpoint (internal_user_endpoints.py)
    participant Helper as _adjust_dates_for_timezone()
    participant Query as _build_where_conditions()
    participant DB as Database (UTC dates)

    UI->>UI: User selects date range (local timezone)
    UI->>UI: formatDate() - Convert to YYYY-MM-DD
    UI->>UI: getTimezoneOffset() - Get timezone offset in minutes
    
    UI->>API: GET /user/daily/activity?start_date=2026-02-04&end_date=2026-02-04&timezone=480
    
    API->>Helper: _adjust_dates_for_timezone(start_date, end_date, 480)
    
    alt timezone > 0 (West of UTC, e.g. PST)
        Helper->>Helper: end_date += 1 day
        Note over Helper: Captures evening records<br/>stored in next UTC day
    else timezone < 0 (East of UTC, e.g. IST)
        Helper->>Helper: start_date -= 1 day
        Note over Helper: Captures morning records<br/>stored in previous UTC day
    else timezone == 0 or None
        Helper->>Helper: No adjustment
    end
    
    Helper-->>API: Return adjusted_start_date, adjusted_end_date
    
    API->>Query: _build_where_conditions() with adjusted dates
    Query->>DB: SELECT * WHERE date >= adjusted_start AND date <= adjusted_end
    DB-->>Query: Return matching UTC records
    Query-->>API: Aggregated results
    API-->>UI: SpendAnalyticsPaginatedResponse
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +1920 to +1924
timezone: Optional[int] = fastapi.Query(
default=None,
description="Timezone offset in minutes from UTC (e.g., 480 for PST). "
"Matches JavaScript's Date.getTimezoneOffset() convention.",
),
Copy link
Contributor

Choose a reason for hiding this comment

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

No validation bounds on timezone parameter - valid timezones range from -720 to +840 minutes

Suggested change
timezone: Optional[int] = fastapi.Query(
default=None,
description="Timezone offset in minutes from UTC (e.g., 480 for PST). "
"Matches JavaScript's Date.getTimezoneOffset() convention.",
),
timezone: Optional[int] = fastapi.Query(
default=None,
ge=-720,
le=840,
description="Timezone offset in minutes from UTC (e.g., 480 for PST). "
"Matches JavaScript's Date.getTimezoneOffset() convention.",
),
Prompt To Fix With AI
This is a comment left during a code review.
Path: litellm/proxy/management_endpoints/internal_user_endpoints.py
Line: 1920:1924

Comment:
No validation bounds on `timezone` parameter - valid timezones range from -720 to +840 minutes

```suggestion
    timezone: Optional[int] = fastapi.Query(
        default=None,
        ge=-720,
        le=840,
        description="Timezone offset in minutes from UTC (e.g., 480 for PST). "
        "Matches JavaScript's Date.getTimezoneOffset() convention.",
    ),
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +2011 to +2015
timezone: Optional[int] = fastapi.Query(
default=None,
description="Timezone offset in minutes from UTC (e.g., 480 for PST). "
"Matches JavaScript's Date.getTimezoneOffset() convention.",
),
Copy link
Contributor

Choose a reason for hiding this comment

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

No validation bounds on timezone parameter

Suggested change
timezone: Optional[int] = fastapi.Query(
default=None,
description="Timezone offset in minutes from UTC (e.g., 480 for PST). "
"Matches JavaScript's Date.getTimezoneOffset() convention.",
),
timezone: Optional[int] = fastapi.Query(
default=None,
ge=-720,
le=840,
description="Timezone offset in minutes from UTC (e.g., 480 for PST). "
"Matches JavaScript's Date.getTimezoneOffset() convention.",
),
Prompt To Fix With AI
This is a comment left during a code review.
Path: litellm/proxy/management_endpoints/internal_user_endpoints.py
Line: 2011:2015

Comment:
No validation bounds on `timezone` parameter

```suggestion
    timezone: Optional[int] = fastapi.Query(
        default=None,
        ge=-720,
        le=840,
        description="Timezone offset in minutes from UTC (e.g., 480 for PST). "
        "Matches JavaScript's Date.getTimezoneOffset() convention.",
    ),
```

How can I resolve this? If you propose a fix, please make it concise.

@yuneng-jiang
Copy link
Collaborator

lgtm

@yuneng-jiang yuneng-jiang merged commit a04a2de into BerriAI:main Feb 5, 2026
6 of 9 checks passed
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.

3 participants