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:
@@ -19,8 +19,11 @@
|
||||
generateAvailableTimeSlots,
|
||||
generateGroupedTimeSlots,
|
||||
formatTime,
|
||||
formatLocalDateTime,
|
||||
calculateEndTime,
|
||||
getDayWithOrdinal,
|
||||
getLondonTodayCalendarDate,
|
||||
parseWallClockDate,
|
||||
type DayHours,
|
||||
type DayAvailability
|
||||
} from '$lib/utils/timeSlots';
|
||||
@@ -35,13 +38,7 @@
|
||||
let { open = $bindable(), booking, onSaved }: Props = $props();
|
||||
|
||||
let saving = $state(false);
|
||||
let placeholder = $state<CalendarDate>(
|
||||
new CalendarDate(
|
||||
new SvelteDate().getFullYear(),
|
||||
new SvelteDate().getMonth() + 1,
|
||||
new SvelteDate().getDate()
|
||||
)
|
||||
);
|
||||
let placeholder = $state<CalendarDate>(getLondonTodayCalendarDate());
|
||||
let selectedDate = $state<CalendarDate | undefined>(undefined);
|
||||
let selectedTime = $state<string | null>(null);
|
||||
let workingHours = $state<Record<string, DayHours> | null>(null);
|
||||
@@ -51,17 +48,17 @@
|
||||
let hoursMonthGeneration = $state(0);
|
||||
let userNavigatedCalendar = $state(false);
|
||||
|
||||
let workingHoursCache: Record<string, Record<string, any>> = {};
|
||||
let availableHoursCache: Record<string, Record<string, any>> = {};
|
||||
let workingHoursCache: Record<string, Record<string, DayHours>> = {};
|
||||
let availableHoursCache: Record<string, Record<string, DayAvailability>> = {};
|
||||
let loadingMonths: Record<string, boolean> = {};
|
||||
let loadingMonthKeys = new SvelteSet<string>();
|
||||
let initialLoadDone = $state(false);
|
||||
let rescheduleAutoSelectDone = $state(false);
|
||||
|
||||
const today = new SvelteDate();
|
||||
const minDate = new CalendarDate(today.getFullYear(), today.getMonth() + 1, today.getDate());
|
||||
const maxDate = new SvelteDate();
|
||||
maxDate.setMonth(today.getMonth() + 6);
|
||||
const today = getLondonTodayCalendarDate();
|
||||
const minDate = today;
|
||||
const maxDate = new SvelteDate(today.year, today.month - 1, today.day);
|
||||
maxDate.setMonth(today.month - 1 + 6);
|
||||
const maxCalendarDate = new CalendarDate(
|
||||
maxDate.getFullYear(),
|
||||
maxDate.getMonth() + 1,
|
||||
@@ -300,11 +297,7 @@
|
||||
loadingMonths = {};
|
||||
loadingMonthKeys = new SvelteSet<string>();
|
||||
rescheduleAutoSelectDone = false;
|
||||
placeholder = new CalendarDate(
|
||||
new SvelteDate().getFullYear(),
|
||||
new SvelteDate().getMonth() + 1,
|
||||
new SvelteDate().getDate()
|
||||
);
|
||||
placeholder = getLondonTodayCalendarDate();
|
||||
}
|
||||
|
||||
async function reschedule() {
|
||||
@@ -315,7 +308,7 @@
|
||||
const localDate = selectedDate.toDate(getLocalTimeZone());
|
||||
const [hours, minutes] = selectedTime.split(':').map(Number);
|
||||
localDate.setHours(hours, minutes, 0, 0);
|
||||
const newStartTime = localDate.toISOString();
|
||||
const newStartTime = formatLocalDateTime(localDate);
|
||||
if (new Date(newStartTime).getTime() <= Date.now()) {
|
||||
toast.error('Start time must be in the future');
|
||||
return;
|
||||
@@ -365,8 +358,8 @@
|
||||
}
|
||||
const key = `${mYear}-${String(mMonth).padStart(2, '0')}`;
|
||||
if (!(key in workingHoursCache)) {
|
||||
workingHoursCache[key] = null as unknown as Record<string, any>;
|
||||
availableHoursCache[key] = null as unknown as Record<string, any>;
|
||||
workingHoursCache[key] = null as unknown as Record<string, DayHours>;
|
||||
availableHoursCache[key] = null as unknown as Record<string, DayAvailability>;
|
||||
loadingMonths[key] = true;
|
||||
}
|
||||
}
|
||||
@@ -397,7 +390,7 @@
|
||||
!rescheduleAutoSelectDone
|
||||
) {
|
||||
rescheduleAutoSelectDone = true;
|
||||
const now = new SvelteDate();
|
||||
const now = new SvelteDate(getLondonTodayCalendarDate().toString() + 'T00:00:00');
|
||||
const maxDateJs = new SvelteDate(
|
||||
maxCalendarDate.year,
|
||||
maxCalendarDate.month - 1,
|
||||
@@ -410,7 +403,7 @@
|
||||
for (let i = 1; i <= daysToCheck; i++) {
|
||||
const checkDate = new SvelteDate(now);
|
||||
checkDate.setDate(now.getDate() + i);
|
||||
const dateStr = checkDate.toISOString().split('T')[0];
|
||||
const dateStr = checkDate.toLocaleDateString('en-CA', { timeZone: 'Europe/London' });
|
||||
const calDate = new CalendarDate(
|
||||
checkDate.getFullYear(),
|
||||
checkDate.getMonth() + 1,
|
||||
@@ -484,12 +477,12 @@
|
||||
<Card.Root class="mb-4">
|
||||
<Card.Content class="pt-4">
|
||||
<div class="text-sm font-medium">
|
||||
Current: {new SvelteDate(booking.start_time).toLocaleDateString('en-GB', {
|
||||
Current: {parseWallClockDate(booking.start_time).toLocaleDateString('en-GB', {
|
||||
weekday: 'short',
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric'
|
||||
})} at {new SvelteDate(booking.start_time).toLocaleTimeString('en-GB', {
|
||||
})} at {parseWallClockDate(booking.start_time).toLocaleTimeString('en-GB', {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
|
||||
Reference in New Issue
Block a user