handle timezones with partial hour offsets

This commit is contained in:
Blake Blackshear 2023-01-16 07:45:53 -06:00
parent 30f520f6f0
commit fc172d67ab
2 changed files with 39 additions and 13 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,16 @@ 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( tz_offset_minutes = int(
datetime.now(pytz.timezone(tz_name.replace(",", "/"))) datetime.now(pytz.timezone(tz_name.replace(",", "/")))
.utcoffset() .utcoffset()
.total_seconds() .total_seconds()
/ 60 / 60
/ 60
) )
parts = year_month.split("-") parts = year_month.split("-")
start_date = datetime( start_date = datetime(
int(parts[0]), int(parts[1]), int(day), int(hour), tzinfo=timezone.utc int(parts[0]), int(parts[1]), int(day), int(hour), tzinfo=timezone.utc
) - timedelta(hours=tz_offset) ) - timedelta(minutes=tz_offset_minutes)
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,12 @@ 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.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