diff --git a/ui/litellm-dashboard/src/components/usage.tsx b/ui/litellm-dashboard/src/components/usage.tsx index 990174ae931..b4987875c17 100644 --- a/ui/litellm-dashboard/src/components/usage.tsx +++ b/ui/litellm-dashboard/src/components/usage.tsx @@ -51,6 +51,7 @@ import { } from "./networking"; import TopKeyView from "./UsagePage/components/EntityUsage/TopKeyView"; import { formatNumberWithCommas } from "@/utils/dataUtils"; +import { formatLocalDate } from "@/utils/dateUtils"; console.log("process.env.NODE_ENV", process.env.NODE_ENV); interface UsagePageProps { @@ -158,8 +159,8 @@ const UsagePage: React.FC = ({ accessToken, token, userRole, use const firstDay = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1); const lastDay = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0); - let startTime = formatDate(firstDay); - let endTime = formatDate(lastDay); + let startTime = formatLocalDate(firstDay); + let endTime = formatLocalDate(lastDay); console.log("keys in usage", keys); console.log("premium user in usage", premiumUser); @@ -233,18 +234,6 @@ const UsagePage: React.FC = ({ accessToken, token, userRole, use console.log("Tag spend data updated successfully"); }; - function formatDate(date: Date) { - const year = date.getFullYear(); - let month = date.getMonth() + 1; // JS month index starts from 0 - let day = date.getDate(); - - // Pad with 0 if month or day is less than 10 - const monthStr = month < 10 ? "0" + month : month; - const dayStr = day < 10 ? "0" + day : day; - - return `${year}-${monthStr}-${dayStr}`; - } - console.log(`Start date is ${startTime}`); console.log(`End date is ${endTime}`); @@ -280,7 +269,7 @@ const UsagePage: React.FC = ({ accessToken, token, userRole, use const year = new Date().getFullYear(); const monthIndex = new Date(`${month} 01 2024`).getMonth(); const fullDate = new Date(year, monthIndex, parseInt(day)); - return formatDate(fullDate); + return formatLocalDate(fullDate); } }; @@ -300,7 +289,7 @@ const UsagePage: React.FC = ({ accessToken, token, userRole, use // Iterate through each date in the range while (currentDate <= endDate) { - const dateStr = formatDate(currentDate); + const dateStr = formatLocalDate(currentDate); if (existingDates.has(dateStr)) { // Use existing data if we have it diff --git a/ui/litellm-dashboard/src/utils/dateUtils.test.ts b/ui/litellm-dashboard/src/utils/dateUtils.test.ts new file mode 100644 index 00000000000..ec7a5f2329a --- /dev/null +++ b/ui/litellm-dashboard/src/utils/dateUtils.test.ts @@ -0,0 +1,23 @@ +import { afterEach, describe, expect, it } from "vitest"; + +import { formatLocalDate } from "./dateUtils"; + +const originalTimezone = process.env.TZ; + +afterEach(() => { + if (originalTimezone === undefined) { + delete process.env.TZ; + } else { + process.env.TZ = originalTimezone; + } +}); + +describe("formatLocalDate", () => { + it("preserves a local midnight ahead of UTC", () => { + process.env.TZ = "Europe/Berlin"; + const localMidnight = new Date(2026, 3, 1); + + expect(localMidnight.toISOString().split("T")[0]).toBe("2026-03-31"); + expect(formatLocalDate(localMidnight)).toBe("2026-04-01"); + }); +}); diff --git a/ui/litellm-dashboard/src/utils/dateUtils.ts b/ui/litellm-dashboard/src/utils/dateUtils.ts new file mode 100644 index 00000000000..ccb707f4a94 --- /dev/null +++ b/ui/litellm-dashboard/src/utils/dateUtils.ts @@ -0,0 +1,8 @@ +/** Format a calendar date without converting it to UTC. */ +export function formatLocalDate(date: Date): string { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const day = String(date.getDate()).padStart(2, "0"); + + return `${year}-${month}-${day}`; +}