summaryrefslogtreecommitdiffstats
path: root/apps/dav/lib/CalDAV
diff options
context:
space:
mode:
authorChristoph Wurst <christoph@winzerhof-wurst.at>2023-01-17 14:04:06 +0100
committerChristoph Wurst <christoph@winzerhof-wurst.at>2023-02-09 15:19:00 +0100
commitb5f7afd0fb79b92371e311917d1ba0547c139dfa (patch)
treecb806f34575a1adc4c2e0523c6111985c274b84c /apps/dav/lib/CalDAV
parentd4a9da0ffdc48c48490e8e6a92297a0459e9018d (diff)
downloadnextcloud-server-b5f7afd0fb79b92371e311917d1ba0547c139dfa.tar.gz
nextcloud-server-b5f7afd0fb79b92371e311917d1ba0547c139dfa.zip
fix(caldav): Fix reminder timezone drift for all-day events
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Diffstat (limited to 'apps/dav/lib/CalDAV')
-rw-r--r--apps/dav/lib/CalDAV/CalDavBackend.php2
-rw-r--r--apps/dav/lib/CalDAV/Reminder/ReminderService.php53
2 files changed, 51 insertions, 4 deletions
diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php
index 51eb505124e..b60d731b215 100644
--- a/apps/dav/lib/CalDAV/CalDavBackend.php
+++ b/apps/dav/lib/CalDAV/CalDavBackend.php
@@ -658,7 +658,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
}
/**
- * @return array{id: int, uri: string, '{http://calendarserver.org/ns/}getctag': string, '{http://sabredav.org/ns}sync-token': int, '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set': SupportedCalendarComponentSet, '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp': ScheduleCalendarTransp }|null
+ * @return array{id: int, uri: string, '{http://calendarserver.org/ns/}getctag': string, '{http://sabredav.org/ns}sync-token': int, '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set': SupportedCalendarComponentSet, '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp': ScheduleCalendarTransp, '{urn:ietf:params:xml:ns:caldav}calendar-timezone': ?string }|null
*/
public function getCalendarById(int $calendarId): ?array {
$fields = array_column($this->propertyMap, 0);
diff --git a/apps/dav/lib/CalDAV/Reminder/ReminderService.php b/apps/dav/lib/CalDAV/Reminder/ReminderService.php
index 1da471a51f5..a2daa3cc98e 100644
--- a/apps/dav/lib/CalDAV/Reminder/ReminderService.php
+++ b/apps/dav/lib/CalDAV/Reminder/ReminderService.php
@@ -32,6 +32,7 @@ declare(strict_types=1);
namespace OCA\DAV\CalDAV\Reminder;
use DateTimeImmutable;
+use DateTimeZone;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\Connector\Sabre\Principal;
use OCP\AppFramework\Utility\ITimeFactory;
@@ -221,6 +222,7 @@ class ReminderService {
if (!$vcalendar) {
return;
}
+ $calendarTimeZone = $this->getCalendarTimeZone((int) $objectData['calendarid']);
$vevents = $this->getAllVEventsFromVCalendar($vcalendar);
if (count($vevents) === 0) {
@@ -249,7 +251,7 @@ class ReminderService {
continue;
}
- $alarms = $this->getRemindersForVAlarm($valarm, $objectData,
+ $alarms = $this->getRemindersForVAlarm($valarm, $objectData, $calendarTimeZone,
$eventHash, $alarmHash, true, true);
$this->writeRemindersToDatabase($alarms);
}
@@ -306,6 +308,16 @@ class ReminderService {
try {
$triggerTime = $valarm->getEffectiveTriggerTime();
+ /**
+ * @psalm-suppress DocblockTypeContradiction
+ * https://github.com/vimeo/psalm/issues/9244
+ */
+ if ($triggerTime->getTimezone() === false || $triggerTime->getTimezone()->getName() === 'UTC') {
+ $triggerTime = new DateTimeImmutable(
+ $triggerTime->format('Y-m-d H:i:s'),
+ $calendarTimeZone
+ );
+ }
} catch (InvalidDataException $e) {
continue;
}
@@ -324,7 +336,7 @@ class ReminderService {
continue;
}
- $alarms = $this->getRemindersForVAlarm($valarm, $objectData, $masterHash, $alarmHash, $isRecurring, false);
+ $alarms = $this->getRemindersForVAlarm($valarm, $objectData, $calendarTimeZone, $masterHash, $alarmHash, $isRecurring, false);
$this->writeRemindersToDatabase($alarms);
$processedAlarms[] = $alarmHash;
}
@@ -363,6 +375,7 @@ class ReminderService {
/**
* @param VAlarm $valarm
* @param array $objectData
+ * @param DateTimeZone $calendarTimeZone
* @param string|null $eventHash
* @param string|null $alarmHash
* @param bool $isRecurring
@@ -371,6 +384,7 @@ class ReminderService {
*/
private function getRemindersForVAlarm(VAlarm $valarm,
array $objectData,
+ DateTimeZone $calendarTimeZone,
string $eventHash = null,
string $alarmHash = null,
bool $isRecurring = false,
@@ -386,6 +400,16 @@ class ReminderService {
$isRelative = $this->isAlarmRelative($valarm);
/** @var DateTimeImmutable $notificationDate */
$notificationDate = $valarm->getEffectiveTriggerTime();
+ /**
+ * @psalm-suppress DocblockTypeContradiction
+ * https://github.com/vimeo/psalm/issues/9244
+ */
+ if ($notificationDate->getTimezone() === false || $notificationDate->getTimezone()->getName() === 'UTC') {
+ $notificationDate = new DateTimeImmutable(
+ $notificationDate->format('Y-m-d H:i:s'),
+ $calendarTimeZone
+ );
+ }
$clonedNotificationDate = new \DateTime('now', $notificationDate->getTimezone());
$clonedNotificationDate->setTimestamp($notificationDate->getTimestamp());
@@ -471,6 +495,7 @@ class ReminderService {
$vevents = $this->getAllVEventsFromVCalendar($vevent->parent);
$recurrenceExceptions = $this->getRecurrenceExceptionFromListOfVEvents($vevents);
$now = $this->timeFactory->getDateTime();
+ $calendarTimeZone = $this->getCalendarTimeZone((int) $reminder['calendar_id']);
try {
$iterator = new EventIterator($vevents, $reminder['uid']);
@@ -517,7 +542,7 @@ class ReminderService {
$alarms = $this->getRemindersForVAlarm($valarm, [
'calendarid' => $reminder['calendar_id'],
'id' => $reminder['object_id'],
- ], $reminder['event_hash'], $alarmHash, true, false);
+ ], $calendarTimeZone, $reminder['event_hash'], $alarmHash, true, false);
$this->writeRemindersToDatabase($alarms);
// Abort generating reminders after creating one successfully
@@ -825,4 +850,26 @@ class ReminderService {
private function isRecurring(VEvent $vevent):bool {
return isset($vevent->RRULE) || isset($vevent->RDATE);
}
+
+ /**
+ * @param int $calendarid
+ *
+ * @return DateTimeZone
+ */
+ private function getCalendarTimeZone(int $calendarid): DateTimeZone {
+ $calendarInfo = $this->caldavBackend->getCalendarById($calendarid);
+ $tzProp = '{urn:ietf:params:xml:ns:caldav}calendar-timezone';
+ if (!isset($calendarInfo[$tzProp])) {
+ // Defaulting to UTC
+ return new DateTimeZone('UTC');
+ }
+ // This property contains a VCALENDAR with a single VTIMEZONE
+ /** @var string $timezoneProp */
+ $timezoneProp = $calendarInfo[$tzProp];
+ /** @var VObject\Component\VCalendar $vtimezoneObj */
+ $vtimezoneObj = VObject\Reader::read($timezoneProp);
+ /** @var VObject\Component\VTimeZone $vtimezone */
+ $vtimezone = $vtimezoneObj->VTIMEZONE;
+ return $vtimezone->getTimeZone();
+ }
}