diff --git a/src/components/settings/__tests__/environment-section-dates.test.tsx b/src/components/settings/__tests__/environment-section-dates.test.tsx
new file mode 100644
index 000000000..a37b6c670
--- /dev/null
+++ b/src/components/settings/__tests__/environment-section-dates.test.tsx
@@ -0,0 +1,58 @@
+/**
+ * The environment settings default their date fields to today. "Today" is
+ * the user's day: the UTC day was yesterday west of UTC in the evening and
+ * tomorrow east of it in the small hours.
+ */
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import { renderToStaticMarkup } from "react-dom/server";
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+
+import { I18nProvider } from "@/lib/i18n/context";
+
+const auth = vi.hoisted(() => ({ timezone: "America/Los_Angeles" }));
+vi.mock("@/hooks/use-auth", () => ({
+ useAuth: () => ({
+ user: { timezone: auth.timezone, modules: { environment: true } },
+ isAuthenticated: true,
+ }),
+}));
+vi.mock("next/navigation", () => ({
+ useRouter: () => ({ push: vi.fn(), replace: vi.fn() }),
+ usePathname: () => "/settings/environment",
+ useSearchParams: () => new URLSearchParams(""),
+}));
+
+import { EnvironmentSection } from "../environment-section";
+
+function backfillEnd(): string | undefined {
+ const client = new QueryClient({ defaultOptions: { queries: { retry: 0 } } });
+ const html = renderToStaticMarkup(
+
+
+
+
+ ,
+ );
+ return /id="env-backfill-end"[^>]*value="([^"]*)"/.exec(html)?.[1];
+}
+
+beforeEach(() => {
+ vi.useFakeTimers({ toFake: ["Date"] });
+});
+afterEach(() => {
+ vi.useRealTimers();
+});
+
+describe(" date defaults", () => {
+ it("defaults to the user's day west of UTC", () => {
+ auth.timezone = "America/Los_Angeles";
+ vi.setSystemTime(new Date("2026-07-03T03:00:00.000Z")); // 2 July, 20:00
+ expect(backfillEnd()).toBe("2026-07-02");
+ });
+
+ it("defaults to the user's day east of UTC", () => {
+ auth.timezone = "Pacific/Kiritimati";
+ vi.setSystemTime(new Date("2026-07-02T12:00:00.000Z")); // 3 July, 02:00
+ expect(backfillEnd()).toBe("2026-07-03");
+ });
+});
diff --git a/src/components/settings/environment-section.tsx b/src/components/settings/environment-section.tsx
index 0a9de3eb6..166500a69 100644
--- a/src/components/settings/environment-section.tsx
+++ b/src/components/settings/environment-section.tsx
@@ -32,6 +32,12 @@ import { formatDateTime } from "@/lib/format";
import { useTranslations } from "@/lib/i18n/context";
import { apiDelete, apiGet, apiPost, apiPut } from "@/lib/api/api-fetch";
import { queryKeys } from "@/lib/query-keys";
+import {
+ DEFAULT_TIMEZONE,
+ detectBrowserTimezone,
+ userDayKey,
+ validTimezoneOr,
+} from "@/lib/tz/format";
interface GeocodeResult {
lat: number;
@@ -72,14 +78,18 @@ interface EnvironmentOverview {
attribution: string;
}
-function todayKey(): string {
- return new Date().toISOString().slice(0, 10);
-}
-
export function EnvironmentSection() {
const { t, tCount } = useTranslations();
const { user } = useAuth();
const queryClient = useQueryClient();
+ // Date defaults are the user's own days. The UTC day was tomorrow for
+ // anyone east of UTC in the small hours and yesterday west of it in the
+ // evening.
+ const tz = validTimezoneOr(
+ user?.timezone ?? detectBrowserTimezone(),
+ DEFAULT_TIMEZONE,
+ );
+ const todayKey = () => userDayKey(new Date(), tz);
const enabled = user?.modules?.environment === true;
@@ -217,7 +227,7 @@ export function EnvironmentSection() {
// user can still type an earlier start, but pre-home days resolve to SKIP on
// the server — to fill the deep past they add an explicit location period.
const effectiveBackfillStart =
- backfillStart || home?.since?.slice(0, 10) || "";
+ backfillStart || (home?.since ? userDayKey(new Date(home.since), tz) : "");
return (