refactor(frontend): timezone-safe date handling with London-aware utilities
Introduce getLondonTodayCalendarDate(), parseWallClockDate(), and formatLocalDateTime() for reliable Europe/London timezone handling. Replace ad-hoc SvelteDate/new Date() usage with these utilities across all components and stores. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
getLunchProtectionForSlots,
|
||||
timeToMinutes
|
||||
} from '$lib/lunchProtection';
|
||||
import { formatLocalDateTime, getLondonTodayCalendarDate } from '$lib/utils/timeSlots';
|
||||
import ClockIcon from '@lucide/svelte/icons/clock';
|
||||
import RefreshCwIcon from '@lucide/svelte/icons/refresh-cw';
|
||||
import ArrowLeftIcon from '@lucide/svelte/icons/arrow-left';
|
||||
@@ -67,10 +68,10 @@
|
||||
let userNavigatedCalendar = $state(false);
|
||||
let editRequestAutoSelectDone = $state(false);
|
||||
// ─── Date constants ─────────────────────────────────────
|
||||
const today = new Date();
|
||||
const minDate = new CalendarDate(today.getFullYear(), today.getMonth() + 1, today.getDate());
|
||||
const maxDate = new Date();
|
||||
maxDate.setMonth(today.getMonth() + 6);
|
||||
const todayCalendarDate = getLondonTodayCalendarDate();
|
||||
const minDate = todayCalendarDate;
|
||||
const maxDate = new SvelteDate(todayCalendarDate.year, todayCalendarDate.month - 1, todayCalendarDate.day);
|
||||
maxDate.setMonth(todayCalendarDate.month - 1 + 6);
|
||||
const maxCalendarDate = new CalendarDate(
|
||||
maxDate.getFullYear(),
|
||||
maxDate.getMonth() + 1,
|
||||
@@ -204,8 +205,7 @@
|
||||
if (!dayWH || !dayWH.isOpen || !dayAH || !dayAH.slots) return [];
|
||||
|
||||
const slots: string[] = [];
|
||||
const now = new SvelteDate();
|
||||
const todayCal = new CalendarDate(now.getFullYear(), now.getMonth() + 1, now.getDate());
|
||||
const todayCal = getLondonTodayCalendarDate();
|
||||
const isToday = date.compare(todayCal) === 0;
|
||||
|
||||
for (const slot of dayAH.slots) {
|
||||
@@ -217,7 +217,10 @@
|
||||
const endMin = eh * 60 + em;
|
||||
|
||||
if (isToday) {
|
||||
const currentMin = now.getHours() * 60 + now.getMinutes();
|
||||
const now = new Date();
|
||||
const londonTime = now.toLocaleTimeString('en-GB', { timeZone: 'Europe/London', hour: '2-digit', minute: '2-digit', hour12: false });
|
||||
const [londonHours, londonMinutes] = londonTime.split(':').map(Number);
|
||||
const currentMin = londonHours * 60 + londonMinutes;
|
||||
startMin = Math.max(startMin, Math.ceil((currentMin + 60) / 15) * 15);
|
||||
}
|
||||
|
||||
@@ -261,11 +264,13 @@
|
||||
let startMin = sh * 60 + sm;
|
||||
const endMin = eh * 60 + em;
|
||||
|
||||
const now = new SvelteDate();
|
||||
const todayCal = new CalendarDate(now.getFullYear(), now.getMonth() + 1, now.getDate());
|
||||
const todayCal = getLondonTodayCalendarDate();
|
||||
const isToday = date.compare(todayCal) === 0;
|
||||
if (isToday) {
|
||||
const currentMin = now.getHours() * 60 + now.getMinutes();
|
||||
const now = new Date();
|
||||
const londonTime = now.toLocaleTimeString('en-GB', { timeZone: 'Europe/London', hour: '2-digit', minute: '2-digit', hour12: false });
|
||||
const [londonHours, londonMinutes] = londonTime.split(':').map(Number);
|
||||
const currentMin = londonHours * 60 + londonMinutes;
|
||||
startMin = Math.max(startMin, Math.ceil((currentMin + 60) / 15) * 15);
|
||||
}
|
||||
|
||||
@@ -324,10 +329,6 @@
|
||||
|
||||
function isDateUnavailable(date: DateValue): boolean {
|
||||
const d = date as CalendarDate;
|
||||
const jsDate = d.toDate(getLocalTimeZone());
|
||||
const now = new SvelteDate();
|
||||
const todayStart = new SvelteDate(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
if (jsDate < todayStart) return true;
|
||||
if (d.compare(minDate) < 0 || d.compare(maxCalendarDate) > 0) return true;
|
||||
if (!workingHours) return true;
|
||||
const dateStr = d.toString();
|
||||
@@ -431,7 +432,7 @@
|
||||
return;
|
||||
editRequestAutoSelectDone = true;
|
||||
|
||||
const currentDate = new SvelteDate();
|
||||
const currentDate = new SvelteDate(getLondonTodayCalendarDate().toString() + 'T00:00:00');
|
||||
const maxDateJs = new SvelteDate(
|
||||
maxCalendarDate.year,
|
||||
maxCalendarDate.month - 1,
|
||||
@@ -446,7 +447,7 @@
|
||||
for (let i = 0; i <= daysToCheck; i++) {
|
||||
const nextDate = new SvelteDate(currentDate);
|
||||
nextDate.setDate(currentDate.getDate() + i);
|
||||
const dateStr = nextDate.toISOString().split('T')[0];
|
||||
const dateStr = nextDate.toLocaleDateString('en-CA', { timeZone: 'Europe/London' });
|
||||
|
||||
if (workingHours[dateStr]?.isOpen) {
|
||||
const calDate = new CalendarDate(
|
||||
@@ -464,11 +465,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
const tomorrow = new SvelteDate();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
newDate = new CalendarDate(tomorrow.getFullYear(), tomorrow.getMonth() + 1, tomorrow.getDate());
|
||||
const tomorrowCal = getLondonTodayCalendarDate();
|
||||
newDate = new CalendarDate(
|
||||
tomorrowCal.year,
|
||||
tomorrowCal.month,
|
||||
tomorrowCal.day + 1
|
||||
);
|
||||
if (!userNavigatedCalendar) {
|
||||
placeholderDate = new CalendarDate(tomorrow.getFullYear(), tomorrow.getMonth() + 1, 1);
|
||||
placeholderDate = new CalendarDate(tomorrowCal.year, tomorrowCal.month, 1);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -612,7 +616,7 @@
|
||||
if (!workingHours || !availableHours) return 0;
|
||||
|
||||
const bookingDate = new SvelteDate(booking.start_time);
|
||||
const dateStr = `${bookingDate.getFullYear()}-${String(bookingDate.getMonth() + 1).padStart(2, '0')}-${String(bookingDate.getDate()).padStart(2, '0')}`;
|
||||
const dateStr = bookingDate.toLocaleDateString('en-CA', { timeZone: 'Europe/London' });
|
||||
|
||||
const dayWH = workingHours[dateStr];
|
||||
const dayAH = availableHours[dateStr];
|
||||
@@ -687,7 +691,7 @@
|
||||
const [hours, minutes] = newTime.split(':').map(Number);
|
||||
const bookingDate = newDate!.toDate(getLocalTimeZone());
|
||||
bookingDate.setHours(hours || 0, minutes || 0, 0, 0);
|
||||
body.new_start_time = bookingDate.toISOString();
|
||||
body.new_start_time = formatLocalDateTime(bookingDate);
|
||||
}
|
||||
|
||||
if (editMode === 'services' || editMode === 'both-time') {
|
||||
|
||||
Reference in New Issue
Block a user