mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-07-24 12:49:01 +03:00
Improve timezone handling (#18257)
* Ensure review activity calendar uses correct timezone react-day-picker 9.x adds a timeZone prop and a TZDate() handler to show the calendar based on a timezone and better handle dates passed to it in timezones * Ensure calendar range uses correct timezone * clean up * ensure range is timezone aware * ensure export dates are timezone aware
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
import { Switch } from "./switch";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { LuCheck } from "react-icons/lu";
|
||||
import { TZDate } from "react-day-picker";
|
||||
import { t } from "i18next";
|
||||
|
||||
export interface DateRangePickerProps {
|
||||
@@ -32,19 +33,24 @@ export interface DateRangePickerProps {
|
||||
locale?: string;
|
||||
/** Option for showing compare feature */
|
||||
showCompare?: boolean;
|
||||
/** timezone */
|
||||
timezone?: string;
|
||||
}
|
||||
|
||||
const getDateAdjustedForTimezone = (dateInput: Date | string): Date => {
|
||||
const getDateAdjustedForTimezone = (
|
||||
dateInput: Date | string,
|
||||
timezone?: string,
|
||||
): Date => {
|
||||
if (typeof dateInput === "string") {
|
||||
// Split the date string to get year, month, and day parts
|
||||
const parts = dateInput.split("-").map((part) => parseInt(part, 10));
|
||||
// Create a new Date object using the local timezone
|
||||
// Note: Month is 0-indexed, so subtract 1 from the month part
|
||||
const date = new Date(parts[0], parts[1] - 1, parts[2]);
|
||||
const date = new TZDate(parts[0], parts[1] - 1, parts[2], timezone);
|
||||
return date;
|
||||
} else {
|
||||
// If dateInput is already a Date object, return it directly
|
||||
return dateInput;
|
||||
return new TZDate(dateInput, timezone);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -73,7 +79,12 @@ const PRESETS: Preset[] = [
|
||||
|
||||
/** The DateRangePicker component allows a user to select a range of dates */
|
||||
export function DateRangePicker({
|
||||
initialDateFrom = new Date(new Date().setHours(0, 0, 0, 0)),
|
||||
timezone,
|
||||
initialDateFrom = (() => {
|
||||
const date = new TZDate(new Date(), timezone);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date;
|
||||
})(),
|
||||
initialDateTo,
|
||||
initialCompareFrom,
|
||||
initialCompareTo,
|
||||
@@ -84,18 +95,27 @@ export function DateRangePicker({
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const [range, setRange] = useState<DateRange>({
|
||||
from: getDateAdjustedForTimezone(initialDateFrom),
|
||||
from: getDateAdjustedForTimezone(initialDateFrom, timezone),
|
||||
to: initialDateTo
|
||||
? getDateAdjustedForTimezone(initialDateTo)
|
||||
: getDateAdjustedForTimezone(initialDateFrom),
|
||||
? getDateAdjustedForTimezone(initialDateTo, timezone)
|
||||
: getDateAdjustedForTimezone(initialDateFrom, timezone),
|
||||
});
|
||||
const [rangeCompare, setRangeCompare] = useState<DateRange | undefined>(
|
||||
initialCompareFrom
|
||||
? {
|
||||
from: new Date(new Date(initialCompareFrom).setHours(0, 0, 0, 0)),
|
||||
from: new TZDate(
|
||||
new Date(initialCompareFrom).setHours(0, 0, 0, 0),
|
||||
timezone,
|
||||
),
|
||||
to: initialCompareTo
|
||||
? new Date(new Date(initialCompareTo).setHours(0, 0, 0, 0))
|
||||
: new Date(new Date(initialCompareFrom).setHours(0, 0, 0, 0)),
|
||||
? new TZDate(
|
||||
new Date(initialCompareTo).setHours(0, 0, 0, 0),
|
||||
timezone,
|
||||
)
|
||||
: new TZDate(
|
||||
new Date(initialCompareFrom).setHours(0, 0, 0, 0),
|
||||
timezone,
|
||||
),
|
||||
}
|
||||
: undefined,
|
||||
);
|
||||
@@ -128,8 +148,8 @@ export function DateRangePicker({
|
||||
const getPresetRange = (presetName: string): DateRange => {
|
||||
const preset = PRESETS.find(({ name }) => name === presetName);
|
||||
if (!preset) throw new Error(`Unknown date range preset: ${presetName}`);
|
||||
const from = new Date();
|
||||
const to = new Date();
|
||||
const from = new TZDate(new Date(), timezone);
|
||||
const to = new TZDate(new Date(), timezone);
|
||||
const first = from.getDate() - from.getDay();
|
||||
|
||||
switch (preset.name) {
|
||||
@@ -191,16 +211,18 @@ export function DateRangePicker({
|
||||
setRange(range);
|
||||
if (rangeCompare) {
|
||||
const rangeCompare = {
|
||||
from: new Date(
|
||||
from: new TZDate(
|
||||
range.from.getFullYear() - 1,
|
||||
range.from.getMonth(),
|
||||
range.from.getDate(),
|
||||
timezone,
|
||||
),
|
||||
to: range.to
|
||||
? new Date(
|
||||
? new TZDate(
|
||||
range.to.getFullYear() - 1,
|
||||
range.to.getMonth(),
|
||||
range.to.getDate(),
|
||||
timezone,
|
||||
)
|
||||
: undefined,
|
||||
};
|
||||
@@ -212,16 +234,18 @@ export function DateRangePicker({
|
||||
for (const preset of PRESETS) {
|
||||
const presetRange = getPresetRange(preset.name);
|
||||
|
||||
const normalizedRangeFrom = new Date(range.from);
|
||||
const normalizedRangeFrom = new TZDate(range.from, timezone);
|
||||
normalizedRangeFrom.setHours(0, 0, 0, 0);
|
||||
const normalizedPresetFrom = new Date(
|
||||
const normalizedPresetFrom = new TZDate(
|
||||
presetRange.from.setHours(0, 0, 0, 0),
|
||||
timezone,
|
||||
);
|
||||
|
||||
const normalizedRangeTo = new Date(range.to ?? 0);
|
||||
const normalizedRangeTo = new TZDate(range.to ?? new Date(0), timezone);
|
||||
normalizedRangeTo.setHours(0, 0, 0, 0);
|
||||
const normalizedPresetTo = new Date(
|
||||
const normalizedPresetTo = new TZDate(
|
||||
presetRange.to?.setHours(0, 0, 0, 0) ?? 0,
|
||||
timezone,
|
||||
);
|
||||
|
||||
if (
|
||||
@@ -401,6 +425,7 @@ export function DateRangePicker({
|
||||
),
|
||||
)
|
||||
}
|
||||
timeZone={timezone}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user