From aebc3f781e32c40faf2ee7fa5a23042e6382ce2e Mon Sep 17 00:00:00 2001
From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>
Date: Fri, 12 Sep 2025 17:12:50 +0900
Subject: perf(frontend): 低精度な現在時刻を一か所で管理するように (#16479)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* perf(frontend): 低精度な現在時刻を一か所で管理するように

* lint

* fix

* remove unused imports

* fix

* Update Changelog

* [ci skip] typo

* enhance: カレンダーウィジェットの日付変更は時間通りに行うように

* [ci skip] fix
---
 packages/frontend/src/widgets/WidgetCalendar.vue | 40 +++++++++++++++++++-----
 1 file changed, 32 insertions(+), 8 deletions(-)

(limited to 'packages/frontend/src/widgets/WidgetCalendar.vue')

diff --git a/packages/frontend/src/widgets/WidgetCalendar.vue b/packages/frontend/src/widgets/WidgetCalendar.vue
index 12c0a66c5c..f2321ca9fa 100644
--- a/packages/frontend/src/widgets/WidgetCalendar.vue
+++ b/packages/frontend/src/widgets/WidgetCalendar.vue
@@ -38,12 +38,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue';
+import { ref, watch } from 'vue';
 import { useWidgetPropsManager } from './widget.js';
 import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import type { FormWithDefault, GetFormResultType } from '@/utility/form.js';
 import { i18n } from '@/i18n.js';
-import { useInterval } from '@@/js/use-interval.js';
+import { useLowresTime, TIME_UPDATE_INTERVAL } from '@/composables/use-lowres-time.js';
 
 const name = 'calendar';
 
@@ -65,6 +65,7 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
 	emit,
 );
 
+const fNow = useLowresTime();
 const year = ref(0);
 const month = ref(0);
 const day = ref(0);
@@ -73,8 +74,14 @@ const yearP = ref(0);
 const monthP = ref(0);
 const dayP = ref(0);
 const isHoliday = ref(false);
-const tick = () => {
-	const now = new Date();
+
+const nextDay = new Date();
+nextDay.setHours(24, 0, 0, 0);
+let nextDayMidnightTime = nextDay.getTime();
+let nextDayTimer: number | null = null;
+
+function update(time: number) {
+	const now = new Date(time);
 	const nd = now.getDate();
 	const nm = now.getMonth();
 	const ny = now.getFullYear();
@@ -104,11 +111,28 @@ const tick = () => {
 	yearP.value = yearNumer / yearDenom * 100;
 
 	isHoliday.value = now.getDay() === 0 || now.getDay() === 6;
-};
+}
+
+watch(fNow, (to) => {
+	update(to);
+
+	// 次回更新までに日付が変わる場合、日付が変わった直後に強制的に更新するタイマーをセットする
+	if (nextDayMidnightTime - to <= TIME_UPDATE_INTERVAL) {
+		if (nextDayTimer != null) {
+			window.clearTimeout(nextDayTimer);
+			nextDayTimer = null;
+		}
+
+		nextDayTimer = window.setTimeout(() => {
+			update(nextDayMidnightTime);
+			nextDayTimer = null;
+		}, nextDayMidnightTime - to);
+	}
+}, { immediate: true });
 
-useInterval(tick, 1000, {
-	immediate: true,
-	afterMounted: false,
+watch(day, () => {
+	nextDay.setHours(24, 0, 0, 0);
+	nextDayMidnightTime = nextDay.getTime();
 });
 
 defineExpose<WidgetComponentExpose>({
-- 
cgit v1.2.3-freya

