1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-22 22:39:36 +02:00

Add timezone support for visits, transportation, and lodging

- Introduced TIMEZONES constant in models.py to store valid timezone options.
- Updated Visit, Transportation, and Lodging models to include timezone fields.
- Modified serializers to include timezone fields in VisitSerializer, TransportationSerializer, and LodgingSerializer.
- Enhanced DateRangeCollapse component to handle timezone selection and formatting.
- Implemented timezone formatting functions in LodgingCard and TransportationCard components.
- Updated LodgingModal and TransportationModal to bind timezone data.
- Added VALID_TIMEZONES to dateUtils for consistent timezone management across the application.
This commit is contained in:
Sean Morley 2025-05-10 11:59:56 -04:00
parent b30d6df964
commit 1323d91e32
12 changed files with 1056 additions and 45 deletions

View file

@ -10,8 +10,8 @@
export let type: 'adventure' | 'transportation' | 'lodging' = 'adventure';
// Initialize with browser's timezone
let selectedStartTimezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone;
let selectedEndTimezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone;
export let selectedStartTimezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone;
export let selectedEndTimezone: string = Intl.DateTimeFormat().resolvedOptions().timeZone;
let allDay: boolean = false;
@ -25,10 +25,19 @@
start_date: string;
end_date: string;
notes: string;
start_timezone?: string;
end_timezone?: string;
timezone: string | null;
};
export let visits: Visit[] | null = null;
type TransportationVisit = {
id: string;
start_date: string;
end_date: string;
notes: string;
start_timezone: string;
end_timezone: string;
};
export let visits: (Visit | TransportationVisit)[] | null = null;
// Local display values
let localStartDate: string = '';
@ -52,6 +61,13 @@
utcDate: utcEndDate,
timezone: type === 'transportation' ? selectedEndTimezone : selectedStartTimezone
}).localDate;
if (!selectedStartTimezone) {
selectedStartTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
}
if (!selectedEndTimezone) {
selectedEndTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
}
});
// Set the full date range for constraining purposes
@ -60,6 +76,22 @@
fullEndDate = `${collection.end_date}T23:59`;
}
function formatDateInTimezone(utcDate: string, timezone: string): string {
try {
return new Intl.DateTimeFormat(undefined, {
timeZone: timezone,
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: true
}).format(new Date(utcDate));
} catch {
return new Date(utcDate).toLocaleString(); // fallback
}
}
// Get constraint dates in the right format based on allDay setting
$: constraintStartDate = allDay
? fullStartDate && fullStartDate.includes('T')
@ -108,22 +140,27 @@
}).utcDate;
}
// Create a visit object with appropriate timezone information
function createVisitObject() {
const newVisit: Visit = {
id: crypto.randomUUID(),
start_date: utcStartDate ?? '',
end_date: utcEndDate ?? utcStartDate ?? '',
notes: note ?? ''
};
// For transportation, add timezone information
function createVisitObject(): Visit | TransportationVisit {
if (type === 'transportation') {
newVisit.start_timezone = selectedStartTimezone;
newVisit.end_timezone = selectedEndTimezone;
const transportVisit: TransportationVisit = {
id: crypto.randomUUID(),
start_date: utcStartDate ?? '',
end_date: utcEndDate ?? utcStartDate ?? '',
notes: note ?? '',
start_timezone: selectedStartTimezone,
end_timezone: selectedEndTimezone
};
return transportVisit;
} else {
const regularVisit: Visit = {
id: crypto.randomUUID(),
start_date: utcStartDate ?? '',
end_date: utcEndDate ?? utcStartDate ?? '',
notes: note ?? '',
timezone: selectedStartTimezone
};
return regularVisit;
}
return newVisit;
}
</script>
@ -369,15 +406,31 @@
{#if isAllDay(visit.start_date)}
<span class="badge badge-outline mr-2">{$t('adventures.all_day')}</span>
{visit.start_date.split('T')[0]} {visit.end_date.split('T')[0]}
{:else if 'start_timezone' in visit}
{formatDateInTimezone(visit.start_date, visit.start_timezone)} {formatDateInTimezone(
visit.end_date,
visit.end_timezone
)}
{:else if visit.timezone}
{formatDateInTimezone(visit.start_date, visit.timezone)} {formatDateInTimezone(
visit.end_date,
visit.timezone
)}
{:else}
{new Date(visit.start_date).toLocaleString()} {new Date(
visit.end_date
).toLocaleString()}
<!-- showe timezones badge -->
{/if}
{#if 'timezone' in visit && visit.timezone}
<span class="badge badge-outline ml-2">{visit.timezone}</span>
{/if}
</p>
<!-- -->
<!-- Display timezone information for transportation visits -->
{#if visit.start_timezone && visit.end_timezone && visit.start_timezone !== visit.end_timezone}
{#if 'start_timezone' in visit && 'end_timezone' in visit && visit.start_timezone !== visit.end_timezone}
<p class="text-xs text-base-content">
{visit.start_timezone}{visit.end_timezone}
</p>
@ -399,8 +452,14 @@
allDay = isAllDayEvent;
// Set timezone information if available
if (visit.start_timezone) selectedStartTimezone = visit.start_timezone;
if (visit.end_timezone) selectedEndTimezone = visit.end_timezone;
if ('start_timezone' in visit) {
// TransportationVisit
selectedStartTimezone = visit.start_timezone;
selectedEndTimezone = visit.end_timezone;
} else if (visit.timezone) {
// Visit
selectedStartTimezone = visit.timezone;
}
if (isAllDayEvent) {
localStartDate = visit.start_date.split('T')[0];
@ -414,7 +473,8 @@
localEndDate = updateLocalDate({
utcDate: visit.end_date,
timezone: visit.end_timezone || selectedStartTimezone
timezone:
'end_timezone' in visit ? visit.end_timezone : selectedStartTimezone
}).localDate;
}

View file

@ -18,6 +18,23 @@
}
}
function formatDateInTimezone(utcDate: string, timezone?: string): string {
if (!utcDate) return '';
try {
return new Intl.DateTimeFormat(undefined, {
timeZone: timezone,
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: true
}).format(new Date(utcDate));
} catch {
return new Date(utcDate).toLocaleString();
}
}
export let lodging: Lodging;
export let user: User | null = null;
export let collection: Collection | null = null;
@ -119,21 +136,9 @@
<div class="flex items-center gap-2">
<span class="font-medium text-sm">{$t('adventures.dates')}:</span>
<p>
{new Date(lodging.check_in).toLocaleString(undefined, {
month: 'short',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: 'numeric'
})}
-
{new Date(lodging.check_out).toLocaleString(undefined, {
month: 'short',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: 'numeric'
})}
{formatDateInTimezone(lodging.check_in ?? '', lodging.timezone ?? undefined)}
{formatDateInTimezone(lodging.check_out ?? '', lodging.timezone ?? undefined)}
<span class="text-xs opacity-60 ml-1">({lodging.timezone})</span>
</p>
</div>
{/if}

View file

@ -56,7 +56,8 @@
is_public: lodgingToEdit?.is_public || false,
collection: lodgingToEdit?.collection || collection.id,
created_at: lodgingToEdit?.created_at || '',
updated_at: lodgingToEdit?.updated_at || ''
updated_at: lodgingToEdit?.updated_at || '',
timezone: lodgingToEdit?.timezone || ''
};
}
@ -303,6 +304,7 @@
type="lodging"
bind:utcStartDate={lodging.check_in}
bind:utcEndDate={lodging.check_out}
bind:selectedStartTimezone={lodging.timezone}
/>
<!-- Location Information -->

View file

@ -18,6 +18,23 @@
}
const dispatch = createEventDispatcher();
function formatDateInTimezone(utcDate: string, timezone?: string): string {
if (!utcDate) return '';
try {
return new Intl.DateTimeFormat(undefined, {
timeZone: timezone,
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: true
}).format(new Date(utcDate));
} catch {
return new Date(utcDate).toLocaleString();
}
}
export let transportation: Transportation;
export let user: User | null = null;
export let collection: Collection | null = null;
@ -136,7 +153,12 @@
{#if transportation.date}
<div class="flex items-center gap-2">
<span class="font-medium text-sm">{$t('adventures.start')}:</span>
<p>{new Date(transportation.date).toLocaleString()}</p>
<p>
{formatDateInTimezone(transportation.date, transportation.start_timezone ?? undefined)}
{#if transportation.start_timezone}
<span class="text-xs opacity-60 ml-1">({transportation.start_timezone})</span>
{/if}
</p>
</div>
{/if}
</div>
@ -154,7 +176,15 @@
{#if transportation.end_date}
<div class="flex items-center gap-2">
<span class="font-medium text-sm">{$t('adventures.end')}:</span>
<p>{new Date(transportation.end_date).toLocaleString()}</p>
<p>
{formatDateInTimezone(
transportation.end_date,
transportation.end_timezone || undefined
)}
{#if transportation.end_timezone}
<span class="text-xs opacity-60 ml-1">({transportation.end_timezone})</span>
{/if}
</p>
</div>
{/if}
</div>

View file

@ -36,7 +36,9 @@
origin_latitude: transportationToEdit?.origin_latitude || NaN,
origin_longitude: transportationToEdit?.origin_longitude || NaN,
destination_latitude: transportationToEdit?.destination_latitude || NaN,
destination_longitude: transportationToEdit?.destination_longitude || NaN
destination_longitude: transportationToEdit?.destination_longitude || NaN,
start_timezone: transportationToEdit?.start_timezone || '',
end_timezone: transportationToEdit?.end_timezone || ''
};
let starting_airport: string = '';
@ -343,6 +345,9 @@
type="transportation"
bind:utcStartDate={transportation.date}
bind:utcEndDate={transportation.end_date}
bind:selectedStartTimezone={transportation.start_timezone}
bind:selectedEndTimezone={transportation.end_timezone}
{collection}
/>
<!-- Flight Information -->