diff --git a/litellm/proxy/spend_tracking/spend_management_endpoints.py b/litellm/proxy/spend_tracking/spend_management_endpoints.py index 8d453bf56ed9..00497c5f45ee 100644 --- a/litellm/proxy/spend_tracking/spend_management_endpoints.py +++ b/litellm/proxy/spend_tracking/spend_management_endpoints.py @@ -1,7 +1,7 @@ #### SPEND MANAGEMENT ##### import collections import os -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone from typing import TYPE_CHECKING, Any, List, Optional import fastapi @@ -1688,13 +1688,18 @@ async def ui_view_spend_logs( # noqa: PLR0915 ) try: + # Convert the date strings to datetime objects - start_date_obj = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S") - end_date_obj = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S") + start_date_obj = datetime.strptime(start_date, "%Y-%m-%d %H:%M:%S").replace( + tzinfo=timezone.utc + ) + end_date_obj = datetime.strptime(end_date, "%Y-%m-%d %H:%M:%S").replace( + tzinfo=timezone.utc + ) # Convert to ISO format strings for Prisma - start_date_iso = start_date_obj.isoformat() + "Z" # Add Z to indicate UTC - end_date_iso = end_date_obj.isoformat() + "Z" # Add Z to indicate UTC + start_date_iso = start_date_obj.isoformat() # Already in UTC, no need to add Z + end_date_iso = end_date_obj.isoformat() # Already in UTC, no need to add Z # Build where conditions where_conditions: dict[str, Any] = { diff --git a/litellm/proxy/spend_tracking/spend_tracking_utils.py b/litellm/proxy/spend_tracking/spend_tracking_utils.py index f918350a889d..ae7c2776ce8b 100644 --- a/litellm/proxy/spend_tracking/spend_tracking_utils.py +++ b/litellm/proxy/spend_tracking/spend_tracking_utils.py @@ -1,6 +1,8 @@ import json import secrets +from datetime import datetime from datetime import datetime as dt +from datetime import timezone from typing import Optional, cast from pydantic import BaseModel @@ -153,9 +155,9 @@ def get_logging_payload( # noqa: PLR0915 call_type=call_type or "", api_key=str(api_key), cache_hit=str(cache_hit), - startTime=start_time, - endTime=end_time, - completionStartTime=completion_start_time, + startTime=_ensure_datetime_utc(start_time), + endTime=_ensure_datetime_utc(end_time), + completionStartTime=_ensure_datetime_utc(completion_start_time), model=kwargs.get("model", "") or "", user=kwargs.get("litellm_params", {}) .get("metadata", {}) @@ -195,6 +197,12 @@ def get_logging_payload( # noqa: PLR0915 raise e +def _ensure_datetime_utc(timestamp: datetime) -> datetime: + """Helper to ensure datetime is in UTC""" + timestamp = timestamp.astimezone(timezone.utc) + return timestamp + + async def get_spend_by_team_and_customer( start_date: dt, end_date: dt, diff --git a/ui/litellm-dashboard/src/components/view_logs/columns.tsx b/ui/litellm-dashboard/src/components/view_logs/columns.tsx index 62f398ce2654..3a55f5177413 100644 --- a/ui/litellm-dashboard/src/components/view_logs/columns.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/columns.tsx @@ -5,6 +5,7 @@ import React from "react"; import { CountryCell } from "./country_cell"; import { getProviderLogoAndName } from "../provider_info_helpers"; import { Tooltip } from "antd"; +import { TimeCell } from "./time_cell"; export type LogEntry = { request_id: string; @@ -53,17 +54,17 @@ export const columns: ColumnDef[] = [ { header: "Time", accessorKey: "startTime", - cell: (info: any) => ( - {moment(info.getValue()).format("MMM DD HH:mm:ss")} - ), + cell: (info: any) => , }, { header: "Request ID", accessorKey: "request_id", cell: (info: any) => ( - - {String(info.getValue() || "")} - + + + {String(info.getValue() || "")} + + ), }, { diff --git a/ui/litellm-dashboard/src/components/view_logs/index.tsx b/ui/litellm-dashboard/src/components/view_logs/index.tsx index c865042c3299..b4691fd3c4b8 100644 --- a/ui/litellm-dashboard/src/components/view_logs/index.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/index.tsx @@ -105,10 +105,11 @@ export default function SpendLogsTable({ }; } - const formattedStartTime = moment(startTime).format("YYYY-MM-DD HH:mm:ss"); + // Convert times to UTC before formatting + const formattedStartTime = moment(startTime).utc().format("YYYY-MM-DD HH:mm:ss"); const formattedEndTime = isCustomDate - ? moment(endTime).format("YYYY-MM-DD HH:mm:ss") - : moment().format("YYYY-MM-DD HH:mm:ss"); + ? moment(endTime).utc().format("YYYY-MM-DD HH:mm:ss") + : moment().utc().format("YYYY-MM-DD HH:mm:ss"); return await uiSpendLogsCall( accessToken, @@ -176,7 +177,7 @@ export default function SpendLogsTable({

Request Logs

-
+
diff --git a/ui/litellm-dashboard/src/components/view_logs/time_cell.tsx b/ui/litellm-dashboard/src/components/view_logs/time_cell.tsx new file mode 100644 index 000000000000..5c0662adb48c --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/time_cell.tsx @@ -0,0 +1,38 @@ +import * as React from "react"; + +interface TimeCellProps { + utcTime: string; +} + +const getLocalTime = (utcTime: string): string => { + try { + const date = new Date(utcTime); + return date.toLocaleString('en-US', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: true + }).replace(',', ''); + } catch (e) { + return "Error converting time"; + } +}; + +export const TimeCell: React.FC = ({ utcTime }) => { + return ( + + {getLocalTime(utcTime)} + + ); +}; + +export const getTimeZone = (): string => { + return Intl.DateTimeFormat().resolvedOptions().timeZone; +}; \ No newline at end of file