handle timezones with partial hour offsets (#5115)

* handle timezones with partial hour offsets

* cleanup
This commit is contained in:
Blake Blackshear 2023-01-16 17:17:03 -06:00 committed by GitHub
parent 81b3fdb423
commit 3bec28ffef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 43 additions and 20 deletions

View File

@ -41,6 +41,7 @@ from frigate.util import (
ffprobe_stream, ffprobe_stream,
restart_frigate, restart_frigate,
vainfo_hwaccel, vainfo_hwaccel,
get_tz_modifiers,
) )
from frigate.storage import StorageMaintainer from frigate.storage import StorageMaintainer
from frigate.version import VERSION from frigate.version import VERSION
@ -91,7 +92,7 @@ def is_healthy():
@bp.route("/events/summary") @bp.route("/events/summary")
def events_summary(): def events_summary():
tz_name = request.args.get("timezone", default="utc", type=str) tz_name = request.args.get("timezone", default="utc", type=str)
tz_offset = f"{int(datetime.now(pytz.timezone(tz_name)).utcoffset().total_seconds()/60/60)} hour" hour_modifier, minute_modifier = get_tz_modifiers(tz_name)
has_clip = request.args.get("has_clip", type=int) has_clip = request.args.get("has_clip", type=int)
has_snapshot = request.args.get("has_snapshot", type=int) has_snapshot = request.args.get("has_snapshot", type=int)
@ -111,7 +112,10 @@ def events_summary():
Event.camera, Event.camera,
Event.label, Event.label,
fn.strftime( fn.strftime(
"%Y-%m-%d", fn.datetime(Event.start_time, "unixepoch", tz_offset) "%Y-%m-%d",
fn.datetime(
Event.start_time, "unixepoch", hour_modifier, minute_modifier
),
).alias("day"), ).alias("day"),
Event.zones, Event.zones,
fn.COUNT(Event.id).alias("count"), fn.COUNT(Event.id).alias("count"),
@ -121,7 +125,10 @@ def events_summary():
Event.camera, Event.camera,
Event.label, Event.label,
fn.strftime( fn.strftime(
"%Y-%m-%d", fn.datetime(Event.start_time, "unixepoch", tz_offset) "%Y-%m-%d",
fn.datetime(
Event.start_time, "unixepoch", hour_modifier, minute_modifier
),
), ),
Event.zones, Event.zones,
) )
@ -912,12 +919,14 @@ def get_recordings_storage_usage():
@bp.route("/<camera_name>/recordings/summary") @bp.route("/<camera_name>/recordings/summary")
def recordings_summary(camera_name): def recordings_summary(camera_name):
tz_name = request.args.get("timezone", default="utc", type=str) tz_name = request.args.get("timezone", default="utc", type=str)
tz_offset = f"{int(datetime.now(pytz.timezone(tz_name)).utcoffset().total_seconds()/60/60)} hour" hour_modifier, minute_modifier = get_tz_modifiers(tz_name)
recording_groups = ( recording_groups = (
Recordings.select( Recordings.select(
fn.strftime( fn.strftime(
"%Y-%m-%d %H", "%Y-%m-%d %H",
fn.datetime(Recordings.start_time, "unixepoch", tz_offset), fn.datetime(
Recordings.start_time, "unixepoch", hour_modifier, minute_modifier
),
).alias("hour"), ).alias("hour"),
fn.SUM(Recordings.duration).alias("duration"), fn.SUM(Recordings.duration).alias("duration"),
fn.SUM(Recordings.motion).alias("motion"), fn.SUM(Recordings.motion).alias("motion"),
@ -927,13 +936,17 @@ def recordings_summary(camera_name):
.group_by( .group_by(
fn.strftime( fn.strftime(
"%Y-%m-%d %H", "%Y-%m-%d %H",
fn.datetime(Recordings.start_time, "unixepoch", tz_offset), fn.datetime(
Recordings.start_time, "unixepoch", hour_modifier, minute_modifier
),
) )
) )
.order_by( .order_by(
fn.strftime( fn.strftime(
"%Y-%m-%d H", "%Y-%m-%d H",
fn.datetime(Recordings.start_time, "unixepoch", tz_offset), fn.datetime(
Recordings.start_time, "unixepoch", hour_modifier, minute_modifier
),
).desc() ).desc()
) )
) )
@ -942,7 +955,9 @@ def recordings_summary(camera_name):
Event.select( Event.select(
fn.strftime( fn.strftime(
"%Y-%m-%d %H", "%Y-%m-%d %H",
fn.datetime(Event.start_time, "unixepoch", tz_offset), fn.datetime(
Event.start_time, "unixepoch", hour_modifier, minute_modifier
),
).alias("hour"), ).alias("hour"),
fn.COUNT(Event.id).alias("count"), fn.COUNT(Event.id).alias("count"),
) )
@ -950,7 +965,9 @@ def recordings_summary(camera_name):
.group_by( .group_by(
fn.strftime( fn.strftime(
"%Y-%m-%d %H", "%Y-%m-%d %H",
fn.datetime(Event.start_time, "unixepoch", tz_offset), fn.datetime(
Event.start_time, "unixepoch", hour_modifier, minute_modifier
),
), ),
) )
.objects() .objects()
@ -1147,17 +1164,11 @@ def vod_hour_no_timezone(year_month, day, hour, camera_name):
# TODO make this nicer when vod module is removed # TODO make this nicer when vod module is removed
@bp.route("/vod/<year_month>/<day>/<hour>/<camera_name>/<tz_name>") @bp.route("/vod/<year_month>/<day>/<hour>/<camera_name>/<tz_name>")
def vod_hour(year_month, day, hour, camera_name, tz_name): def vod_hour(year_month, day, hour, camera_name, tz_name):
tz_offset = int(
datetime.now(pytz.timezone(tz_name.replace(",", "/")))
.utcoffset()
.total_seconds()
/ 60
/ 60
)
parts = year_month.split("-") parts = year_month.split("-")
start_date = datetime( start_date = (
int(parts[0]), int(parts[1]), int(day), int(hour), tzinfo=timezone.utc datetime(int(parts[0]), int(parts[1]), int(day), int(hour), tzinfo=timezone.utc)
) - timedelta(hours=tz_offset) - datetime.now(pytz.timezone(tz_name.replace(",", "/"))).utcoffset()
)
end_date = start_date + timedelta(hours=1) - timedelta(milliseconds=1) end_date = start_date + timedelta(hours=1) - timedelta(milliseconds=1)
start_ts = start_date.timestamp() start_ts = start_date.timestamp()
end_ts = end_date.timestamp() end_ts = end_date.timestamp()

View File

@ -14,12 +14,13 @@ from abc import ABC, abstractmethod
from collections import Counter from collections import Counter
from collections.abc import Mapping from collections.abc import Mapping
from multiprocessing import shared_memory from multiprocessing import shared_memory
from typing import Any, AnyStr from typing import Any, AnyStr, Tuple
import cv2 import cv2
import numpy as np import numpy as np
import os import os
import psutil import psutil
import pytz
from frigate.const import REGEX_HTTP_CAMERA_USER_PASS, REGEX_RTSP_CAMERA_USER_PASS from frigate.const import REGEX_HTTP_CAMERA_USER_PASS, REGEX_RTSP_CAMERA_USER_PASS
@ -1040,3 +1041,14 @@ class SharedMemoryFrameManager(FrameManager):
self.shm_store[name].close() self.shm_store[name].close()
self.shm_store[name].unlink() self.shm_store[name].unlink()
del self.shm_store[name] del self.shm_store[name]
def get_tz_modifiers(tz_name: str) -> Tuple[str, str]:
seconds_offset = (
datetime.datetime.now(pytz.timezone(tz_name)).utcoffset().total_seconds()
)
hours_offset = int(seconds_offset / 60 / 60)
minutes_offset = int(seconds_offset / 60 - hours_offset * 60)
hour_modifier = f"{hours_offset} hour"
minute_modifier = f"{minutes_offset} minute"
return hour_modifier, minute_modifier