From 3bec28ffef8c4b57d760ac8b10d9ba7cf01b56b5 Mon Sep 17 00:00:00 2001 From: Blake Blackshear Date: Mon, 16 Jan 2023 17:17:03 -0600 Subject: [PATCH] handle timezones with partial hour offsets (#5115) * handle timezones with partial hour offsets * cleanup --- frigate/http.py | 49 ++++++++++++++++++++++++++++++------------------- frigate/util.py | 14 +++++++++++++- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/frigate/http.py b/frigate/http.py index 815c700cc..a83b94751 100644 --- a/frigate/http.py +++ b/frigate/http.py @@ -41,6 +41,7 @@ from frigate.util import ( ffprobe_stream, restart_frigate, vainfo_hwaccel, + get_tz_modifiers, ) from frigate.storage import StorageMaintainer from frigate.version import VERSION @@ -91,7 +92,7 @@ def is_healthy(): @bp.route("/events/summary") def events_summary(): 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_snapshot = request.args.get("has_snapshot", type=int) @@ -111,7 +112,10 @@ def events_summary(): Event.camera, Event.label, 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"), Event.zones, fn.COUNT(Event.id).alias("count"), @@ -121,7 +125,10 @@ def events_summary(): Event.camera, Event.label, 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, ) @@ -912,12 +919,14 @@ def get_recordings_storage_usage(): @bp.route("//recordings/summary") def recordings_summary(camera_name): 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 = ( Recordings.select( fn.strftime( "%Y-%m-%d %H", - fn.datetime(Recordings.start_time, "unixepoch", tz_offset), + fn.datetime( + Recordings.start_time, "unixepoch", hour_modifier, minute_modifier + ), ).alias("hour"), fn.SUM(Recordings.duration).alias("duration"), fn.SUM(Recordings.motion).alias("motion"), @@ -927,13 +936,17 @@ def recordings_summary(camera_name): .group_by( fn.strftime( "%Y-%m-%d %H", - fn.datetime(Recordings.start_time, "unixepoch", tz_offset), + fn.datetime( + Recordings.start_time, "unixepoch", hour_modifier, minute_modifier + ), ) ) .order_by( fn.strftime( "%Y-%m-%d H", - fn.datetime(Recordings.start_time, "unixepoch", tz_offset), + fn.datetime( + Recordings.start_time, "unixepoch", hour_modifier, minute_modifier + ), ).desc() ) ) @@ -942,7 +955,9 @@ def recordings_summary(camera_name): Event.select( fn.strftime( "%Y-%m-%d %H", - fn.datetime(Event.start_time, "unixepoch", tz_offset), + fn.datetime( + Event.start_time, "unixepoch", hour_modifier, minute_modifier + ), ).alias("hour"), fn.COUNT(Event.id).alias("count"), ) @@ -950,7 +965,9 @@ def recordings_summary(camera_name): .group_by( fn.strftime( "%Y-%m-%d %H", - fn.datetime(Event.start_time, "unixepoch", tz_offset), + fn.datetime( + Event.start_time, "unixepoch", hour_modifier, minute_modifier + ), ), ) .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 @bp.route("/vod/////") 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("-") - start_date = datetime( - int(parts[0]), int(parts[1]), int(day), int(hour), tzinfo=timezone.utc - ) - timedelta(hours=tz_offset) + start_date = ( + datetime(int(parts[0]), int(parts[1]), int(day), int(hour), tzinfo=timezone.utc) + - datetime.now(pytz.timezone(tz_name.replace(",", "/"))).utcoffset() + ) end_date = start_date + timedelta(hours=1) - timedelta(milliseconds=1) start_ts = start_date.timestamp() end_ts = end_date.timestamp() diff --git a/frigate/util.py b/frigate/util.py index 6de537a0d..69ead2a7a 100755 --- a/frigate/util.py +++ b/frigate/util.py @@ -14,12 +14,13 @@ from abc import ABC, abstractmethod from collections import Counter from collections.abc import Mapping from multiprocessing import shared_memory -from typing import Any, AnyStr +from typing import Any, AnyStr, Tuple import cv2 import numpy as np import os import psutil +import pytz 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].unlink() 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