mirror of
https://github.com/alextselegidis/easyappointments.git
synced 2024-12-21 06:02:23 +03:00
Incorrect Timezone Handling in CalDAV Synchronization Causes Time Shifts (#1626)
This commit is contained in:
parent
b2ed516b5f
commit
8e14176e52
2 changed files with 65 additions and 28 deletions
|
@ -17,6 +17,7 @@ developers to maintain and readjust their custom modifications on the main proje
|
||||||
- Fix various 1.5.0 API issues (#1562)
|
- Fix various 1.5.0 API issues (#1562)
|
||||||
- Correct email issues by replacing the internal email library with phpmailer (#1587)
|
- Correct email issues by replacing the internal email library with phpmailer (#1587)
|
||||||
- Fix ICS file mimetype (#1630)
|
- Fix ICS file mimetype (#1630)
|
||||||
|
- Incorrect Timezone Handling in CalDAV Synchronization Causes Time Shifts (#1626)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -457,6 +457,37 @@ class Caldav_sync
|
||||||
return str_replace('METHOD:PUBLISH', '', $ics_file);
|
return str_replace('METHOD:PUBLISH', '', $ics_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to parse the CalDAV event date-time value with the right timezone.
|
||||||
|
*
|
||||||
|
* @throws DateMalformedStringException
|
||||||
|
* @throws DateInvalidTimeZoneException
|
||||||
|
*/
|
||||||
|
private function parse_date_time_object(string $caldav_date_time, DateTimeZone $default_timezone_object): DateTime
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (str_contains($caldav_date_time, 'TZID=')) {
|
||||||
|
// Extract the TZID and use it
|
||||||
|
preg_match('/TZID=([^:]+):/', $caldav_date_time, $matches);
|
||||||
|
$parsed_timezone = $matches[1];
|
||||||
|
$parsed_timezone_object = new DateTimeZone($parsed_timezone);
|
||||||
|
$date_time = preg_replace('/TZID=[^:]+:/', '', $caldav_date_time);
|
||||||
|
$date_time_object = new DateTime($date_time, $parsed_timezone_object);
|
||||||
|
} elseif (str_ends_with($caldav_date_time, 'Z')) {
|
||||||
|
// Handle UTC timestamps
|
||||||
|
$date_time_object = new DateTime($caldav_date_time, new DateTimeZone('UTC'));
|
||||||
|
} else {
|
||||||
|
// Default to the provided timezone
|
||||||
|
$date_time_object = new DateTime($caldav_date_time, $default_timezone_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $date_time_object;
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
error_log('Error parsing date-time value (' . $caldav_date_time . ') with timezone: ' . $e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert the VEvent object to an associative array
|
* Convert the VEvent object to an associative array
|
||||||
*
|
*
|
||||||
|
@ -467,45 +498,50 @@ class Caldav_sync
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*
|
*
|
||||||
* @throws DateMalformedStringException
|
* @throws Throwable
|
||||||
*/
|
*/
|
||||||
private function convert_caldav_event_to_array_event(VEvent $vevent, DateTimeZone $timezone_object): array
|
private function convert_caldav_event_to_array_event(VEvent $vevent, DateTimeZone $timezone_object): array
|
||||||
{
|
{
|
||||||
$utc_timezone_object = new DateTimeZone('UTC'); // Convert from UTC to local provider timezone
|
try {
|
||||||
|
$caldav_start_date_time = (string) $vevent->DTSTART;
|
||||||
|
$start_date_time_object = $this->parse_date_time_object($caldav_start_date_time, $timezone_object);
|
||||||
|
$start_date_time_object->setTimezone($timezone_object); // Convert to the provider timezone
|
||||||
|
|
||||||
$start_date_time_object = new DateTime((string) $vevent->DTSTART, $utc_timezone_object);
|
$caldav_end_date_time = (string) $vevent->DTEND;
|
||||||
$start_date_time_object->setTimezone($timezone_object);
|
$end_date_time_object = $this->parse_date_time_object($caldav_end_date_time, $timezone_object);
|
||||||
|
$end_date_time_object->setTimezone($timezone_object); // Convert to the provider timezone
|
||||||
|
|
||||||
$end_date_time_object = new DateTime((string) $vevent->DTEND, $utc_timezone_object);
|
// Check if the event is recurring
|
||||||
$end_date_time_object->setTimezone($timezone_object);
|
|
||||||
|
|
||||||
// Check if the event is recurring
|
$is_recurring_event =
|
||||||
|
isset($vevent->RRULE) ||
|
||||||
|
isset($vevent->RDATE) ||
|
||||||
|
isset($vevent->{'RECURRENCE-ID'}) ||
|
||||||
|
isset($vevent->EXDATE);
|
||||||
|
|
||||||
$is_recurring_event =
|
// Generate ID based on recurrence status
|
||||||
isset($vevent->RRULE) ||
|
|
||||||
isset($vevent->RDATE) ||
|
|
||||||
isset($vevent->{'RECURRENCE-ID'}) ||
|
|
||||||
isset($vevent->EXDATE);
|
|
||||||
|
|
||||||
// Generate ID based on recurrence status
|
$event_id = (string) $vevent->UID;
|
||||||
|
|
||||||
$event_id = (string) $vevent->UID;
|
if ($is_recurring_event) {
|
||||||
|
$event_id .= '-RECURRENCE-' . random_string();
|
||||||
|
}
|
||||||
|
|
||||||
if ($is_recurring_event) {
|
// Return the converted event
|
||||||
$event_id .= '-RECURRENCE-' . random_string();
|
|
||||||
|
return [
|
||||||
|
'id' => $event_id,
|
||||||
|
'summary' => (string) $vevent->SUMMARY ?? null ?: '',
|
||||||
|
'start_datetime' => $start_date_time_object->format('Y-m-d H:i:s'),
|
||||||
|
'end_datetime' => $end_date_time_object->format('Y-m-d H:i:s'),
|
||||||
|
'description' => (string) $vevent->DESCRIPTION ?? null ?: '',
|
||||||
|
'status' => (string) $vevent->STATUS ?? null ?: 'CONFIRMED',
|
||||||
|
'location' => (string) $vevent->LOCATION ?? null ?: '',
|
||||||
|
];
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
error_log('Error parsing CalDAV event object (' . var_export($vevent, true) . '): ' . $e->getMessage());
|
||||||
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the converted event
|
|
||||||
|
|
||||||
return [
|
|
||||||
'id' => $event_id,
|
|
||||||
'summary' => (string) $vevent->SUMMARY,
|
|
||||||
'start_datetime' => $start_date_time_object->format('Y-m-d H:i:s'),
|
|
||||||
'end_datetime' => $end_date_time_object->format('Y-m-d H:i:s'),
|
|
||||||
'description' => (string) $vevent->DESCRIPTION,
|
|
||||||
'status' => (string) $vevent->STATUS,
|
|
||||||
'location' => (string) $vevent->LOCATION,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue