mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
Events Page: Added option to override browser time format and style (#5538)
* use12hour optional config * use12hour config arg * added use12HourFormat arg to format function * dateStyle & timeStyle option * moved timezone & locales to format function * added dateStyle & timeStyle * re-formatted * added strftime_fmt config entry * strftime package * added strftime option * underscore instead of camelCase * underscore props instead of camelCase
This commit is contained in:
parent
fbf29667d4
commit
3611e874ca
@ -66,12 +66,29 @@ class LiveModeEnum(str, Enum):
|
||||
webrtc = "webrtc"
|
||||
|
||||
|
||||
class DateTimeStyleEnum(str, Enum):
|
||||
full = "full"
|
||||
long = "long"
|
||||
medium = "medium"
|
||||
short = "short"
|
||||
|
||||
|
||||
class UIConfig(FrigateBaseModel):
|
||||
live_mode: LiveModeEnum = Field(
|
||||
default=LiveModeEnum.mse, title="Default Live Mode."
|
||||
)
|
||||
timezone: Optional[str] = Field(title="Override UI timezone.")
|
||||
use_experimental: bool = Field(default=False, title="Experimental UI")
|
||||
use12hour: Optional[bool] = Field(title="Override UI time format.")
|
||||
date_style: DateTimeStyleEnum = Field(
|
||||
default=DateTimeStyleEnum.short, title="Override UI dateStyle."
|
||||
)
|
||||
time_style: DateTimeStyleEnum = Field(
|
||||
default=DateTimeStyleEnum.medium, title="Override UI timeStyle."
|
||||
)
|
||||
strftime_fmt: Optional[str] = Field(
|
||||
default=None, title="Override date and time format using strftime syntax."
|
||||
)
|
||||
|
||||
|
||||
class TelemetryConfig(FrigateBaseModel):
|
||||
|
41
web/package-lock.json
generated
41
web/package-lock.json
generated
@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"@cycjimmy/jsmpeg-player": "^6.0.5",
|
||||
"axios": "^1.2.2",
|
||||
"copy-to-clipboard": "3.3.3",
|
||||
"date-fns": "^2.29.3",
|
||||
"idb-keyval": "^6.2.0",
|
||||
"immer": "^9.0.16",
|
||||
@ -19,6 +20,7 @@
|
||||
"preact-router": "^4.1.0",
|
||||
"react": "npm:@preact/compat@^17.1.2",
|
||||
"react-dom": "npm:@preact/compat@^17.1.2",
|
||||
"strftime": "^0.10.1",
|
||||
"swr": "^1.3.0",
|
||||
"video.js": "^7.20.3",
|
||||
"videojs-playlist": "^5.0.0",
|
||||
@ -3369,6 +3371,14 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/copy-to-clipboard": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
|
||||
"integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
|
||||
"dependencies": {
|
||||
"toggle-selection": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "3.26.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.0.tgz",
|
||||
@ -8547,6 +8557,14 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strftime": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/strftime/-/strftime-0.10.1.tgz",
|
||||
"integrity": "sha512-nVvH6JG8KlXFPC0f8lojLgEsPA18lRpLZ+RrJh/NkQV2tqOgZfbas8gcU8SFgnnqR3rWzZPYu6N2A3xzs/8rQg==",
|
||||
"engines": {
|
||||
"node": ">=0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/strict-event-emitter": {
|
||||
"version": "0.2.8",
|
||||
"resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.2.8.tgz",
|
||||
@ -8901,6 +8919,11 @@
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/toggle-selection": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
|
||||
"integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="
|
||||
},
|
||||
"node_modules/totalist": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.0.tgz",
|
||||
@ -12151,6 +12174,14 @@
|
||||
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
|
||||
"dev": true
|
||||
},
|
||||
"copy-to-clipboard": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
|
||||
"integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
|
||||
"requires": {
|
||||
"toggle-selection": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"core-js": {
|
||||
"version": "3.26.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.0.tgz",
|
||||
@ -15868,6 +15899,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"strftime": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/strftime/-/strftime-0.10.1.tgz",
|
||||
"integrity": "sha512-nVvH6JG8KlXFPC0f8lojLgEsPA18lRpLZ+RrJh/NkQV2tqOgZfbas8gcU8SFgnnqR3rWzZPYu6N2A3xzs/8rQg=="
|
||||
},
|
||||
"strict-event-emitter": {
|
||||
"version": "0.2.8",
|
||||
"resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.2.8.tgz",
|
||||
@ -16154,6 +16190,11 @@
|
||||
"is-number": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"toggle-selection": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
|
||||
"integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ=="
|
||||
},
|
||||
"totalist": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.0.tgz",
|
||||
|
@ -18,17 +18,18 @@
|
||||
"date-fns": "^2.29.3",
|
||||
"idb-keyval": "^6.2.0",
|
||||
"immer": "^9.0.16",
|
||||
"monaco-yaml": "^4.0.2",
|
||||
"preact": "^10.11.3",
|
||||
"preact-async-route": "^2.2.1",
|
||||
"preact-router": "^4.1.0",
|
||||
"react": "npm:@preact/compat@^17.1.2",
|
||||
"react-dom": "npm:@preact/compat@^17.1.2",
|
||||
"vite-plugin-monaco-editor": "^1.1.0",
|
||||
"monaco-yaml": "^4.0.2",
|
||||
"strftime": "^0.10.1",
|
||||
"swr": "^1.3.0",
|
||||
"video.js": "^7.20.3",
|
||||
"videojs-playlist": "^5.0.0",
|
||||
"videojs-seek-buttons": "^3.0.1"
|
||||
"videojs-seek-buttons": "^3.0.1",
|
||||
"vite-plugin-monaco-editor": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@preact/preset-vite": "^2.5.0",
|
||||
|
@ -287,9 +287,6 @@ export default function Events({ path, ...props }) {
|
||||
return <ActivityIndicator />;
|
||||
}
|
||||
|
||||
const timezone = config.ui?.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone;
|
||||
const locale = window.navigator?.language || 'en-US';
|
||||
|
||||
return (
|
||||
<div className="space-y-4 p-2 px-4 w-full">
|
||||
<Heading>Events</Heading>
|
||||
@ -508,7 +505,7 @@ export default function Events({ path, ...props }) {
|
||||
</div>
|
||||
<div className="text-sm flex">
|
||||
<Clock className="h-5 w-5 mr-2 inline" />
|
||||
{formatUnixTimestampToDateTime(event.start_time, locale, timezone)}
|
||||
{formatUnixTimestampToDateTime(event.start_time, { ...config.ui })}
|
||||
<div className="hidden md:inline">
|
||||
<span className="m-1">-</span>
|
||||
<TimeAgo time={event.start_time * 1000} dense />
|
||||
|
@ -1,7 +1,8 @@
|
||||
import strftime from 'strftime';
|
||||
import { fromUnixTime, intervalToDuration, formatDuration } from 'date-fns';
|
||||
export const longToDate = (long: number): Date => new Date(long * 1000);
|
||||
export const epochToLong = (date: number): number => date / 1000;
|
||||
export const dateToLong = (date: Date): number => epochToLong(date.getTime());
|
||||
import { fromUnixTime, intervalToDuration, formatDuration } from 'date-fns';
|
||||
|
||||
const getDateTimeYesterday = (dateTime: Date): Date => {
|
||||
const twentyFourHoursInMilliseconds = 24 * 60 * 60 * 1000;
|
||||
@ -17,28 +18,53 @@ export const getNowYesterdayInLong = (): number => {
|
||||
};
|
||||
|
||||
/**
|
||||
* This function takes in a unix timestamp, locale, timezone,
|
||||
* and returns a dateTime string.
|
||||
* If unixTimestamp is not provided, it returns 'Invalid time'
|
||||
* @param unixTimestamp: number
|
||||
* @param locale: string
|
||||
* @param timezone: string
|
||||
* @returns string - dateTime or 'Invalid time' if unixTimestamp is not provided
|
||||
* This function takes in a Unix timestamp, configuration options for date/time display, and an optional strftime format string,
|
||||
* and returns a formatted date/time string.
|
||||
*
|
||||
* If the Unix timestamp is not provided, it returns "Invalid time".
|
||||
*
|
||||
* The configuration options determine how the date and time are formatted.
|
||||
* The `timezone` option allows you to specify a specific timezone for the output, otherwise the user's browser timezone will be used.
|
||||
* The `use12hour` option allows you to display time in a 12-hour format if true, and 24-hour format if false.
|
||||
* The `dateStyle` and `timeStyle` options allow you to specify pre-defined formats for displaying the date and time.
|
||||
* The `strftime_fmt` option allows you to specify a custom format using the strftime syntax.
|
||||
*
|
||||
* If both `strftime_fmt` and `dateStyle`/`timeStyle` are provided, `strftime_fmt` takes precedence.
|
||||
*
|
||||
* @param unixTimestamp The Unix timestamp to format
|
||||
* @param config An object containing the configuration options for date/time display
|
||||
* @returns The formatted date/time string, or "Invalid time" if the Unix timestamp is not provided or invalid.
|
||||
*/
|
||||
export const formatUnixTimestampToDateTime = (unixTimestamp: number, locale: string, timezone: string): string => {
|
||||
interface DateTimeStyle {
|
||||
timezone: string;
|
||||
use12hour: boolean | undefined;
|
||||
date_style: 'full' | 'long' | 'medium' | 'short';
|
||||
time_style: 'full' | 'long' | 'medium' | 'short';
|
||||
strftime_fmt: string;
|
||||
}
|
||||
|
||||
export const formatUnixTimestampToDateTime = (unixTimestamp: number, config: DateTimeStyle): string => {
|
||||
const { timezone, use12hour, date_style, time_style, strftime_fmt } = config;
|
||||
const locale = window.navigator?.language || 'en-US';
|
||||
|
||||
if (isNaN(unixTimestamp)) {
|
||||
return 'Invalid time';
|
||||
}
|
||||
try {
|
||||
const date = new Date(unixTimestamp * 1000);
|
||||
|
||||
// use strftime_fmt if defined in config file
|
||||
if (strftime_fmt) {
|
||||
const strftime_locale = strftime.localizeByIdentifier(locale);
|
||||
return strftime_locale(strftime_fmt, date);
|
||||
}
|
||||
|
||||
// else use Intl.DateTimeFormat
|
||||
const formatter = new Intl.DateTimeFormat(locale, {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
timeZone: timezone,
|
||||
dateStyle: date_style,
|
||||
timeStyle: time_style,
|
||||
timeZone: timezone || Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
hour12: use12hour !== null ? use12hour : undefined,
|
||||
});
|
||||
return formatter.format(date);
|
||||
} catch (error) {
|
||||
|
Loading…
Reference in New Issue
Block a user