Incorrect Timezone Handling in CalDAV Synchronization Causes Time Shifts (#1626)

This commit is contained in:
Alex Tselegidis 2024-12-19 21:01:47 +02:00
parent b2ed516b5f
commit 8e14176e52
2 changed files with 65 additions and 28 deletions

View file

@ -17,6 +17,7 @@ developers to maintain and readjust their custom modifications on the main proje
- Fix various 1.5.0 API issues (#1562)
- Correct email issues by replacing the internal email library with phpmailer (#1587)
- Fix ICS file mimetype (#1630)
- Incorrect Timezone Handling in CalDAV Synchronization Causes Time Shifts (#1626)

View file

@ -457,6 +457,37 @@ class Caldav_sync
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
*
@ -467,17 +498,18 @@ class Caldav_sync
*
* @return array
*
* @throws DateMalformedStringException
* @throws Throwable
*/
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);
$start_date_time_object->setTimezone($timezone_object);
$end_date_time_object = new DateTime((string) $vevent->DTEND, $utc_timezone_object);
$end_date_time_object->setTimezone($timezone_object);
$caldav_end_date_time = (string) $vevent->DTEND;
$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
// Check if the event is recurring
@ -499,13 +531,17 @@ class Caldav_sync
return [
'id' => $event_id,
'summary' => (string) $vevent->SUMMARY,
'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,
'status' => (string) $vevent->STATUS,
'location' => (string) $vevent->LOCATION,
'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;
}
}
/**