From e84231e4c1d99c63fb307814914bf85a9a3ee727 Mon Sep 17 00:00:00 2001 From: atymic Date: Fri, 26 Jan 2024 09:26:05 +0100 Subject: [PATCH] fix: all day event issues --- .gitignore | 1 + src/Generators/Ics.php | 4 +-- src/Link.php | 13 +++++++++ tests/Generators/GoogleGeneratorTest.php | 16 +++++++++++ tests/Generators/IcsGeneratorTest.php | 16 +++++++++++ ...y_generates_all_day_events_by_dates__1.txt | 1 + ...ly_generates_all_day_events_by_days__1.txt | 1 + ...y_generates_all_day_events_by_dates__1.txt | 12 +++++++++ ...ly_generates_all_day_events_by_days__1.txt | 12 +++++++++ tests/TestCase.php | 27 +++++++++++++++++++ 10 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 tests/Generators/__snapshots__/GoogleGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt create mode 100644 tests/Generators/__snapshots__/GoogleGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt create mode 100644 tests/Generators/__snapshots__/IcsGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt create mode 100644 tests/Generators/__snapshots__/IcsGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt diff --git a/.gitignore b/.gitignore index b240708..2a712b7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ vendor coverage .phpunit.result.cache .php-cs-fixer.cache +.phpunit.cache diff --git a/src/Generators/Ics.php b/src/Generators/Ics.php index b738974..3db48e3 100644 --- a/src/Generators/Ics.php +++ b/src/Generators/Ics.php @@ -41,8 +41,8 @@ public function generate(Link $link): string $dateTimeFormat = $link->allDay ? $this->dateFormat : $this->dateTimeFormat; if ($link->allDay) { - $url[] = 'DTSTAMP:'.gmdate($dateTimeFormat, $link->from->getTimestamp()); - $url[] = 'DTSTART:'.gmdate($dateTimeFormat, $link->from->getTimestamp()); + $url[] = 'DTSTAMP:'.$link->from->format($dateTimeFormat); + $url[] = 'DTSTART:'.$link->from->format($dateTimeFormat); $url[] = 'DURATION:P'.(max(1, $link->from->diff($link->to)->days)).'D'; } else { $url[] = 'DTSTAMP:'.gmdate($dateTimeFormat, $link->from->getTimestamp()); diff --git a/src/Link.php b/src/Link.php index 56ab3c3..821b0e3 100644 --- a/src/Link.php +++ b/src/Link.php @@ -61,6 +61,14 @@ public function __construct(string $title, \DateTimeInterface $from, \DateTimeIn */ public static function create(string $title, \DateTimeInterface $from, \DateTimeInterface $to, bool $allDay = false) { + // When creating all day events, we need to be in the UTC timezone as all day events are "floating" based on the user's timezone + if ($allDay) { + $startDate = new \DateTime($from->format('Y-m-d'), new \DateTimeZone('UTC')); + $numberOfDays = $from->diff($to)->days + 1; + + return self::createAllDay($title, $startDate, $numberOfDays); + } + return new static($title, $from, $to, $allDay); } @@ -74,6 +82,11 @@ public static function create(string $title, \DateTimeInterface $from, \DateTime */ public static function createAllDay(string $title, \DateTimeInterface $fromDate, int $numberOfDays = 1): self { + // In cases where the from date is not UTC, make sure it's UTC, size all day events are floating and non UTC dates cause bugs in the generators + if ($fromDate->getTimezone() !== new \DateTimeZone('UTC')) { + $fromDate = \DateTime::createFromFormat('Y-m-d', $fromDate->format('Y-m-d')); + } + $from = (clone $fromDate)->modify('midnight'); $to = (clone $from)->modify("+$numberOfDays days"); diff --git a/tests/Generators/GoogleGeneratorTest.php b/tests/Generators/GoogleGeneratorTest.php index 9d87a12..079e15b 100644 --- a/tests/Generators/GoogleGeneratorTest.php +++ b/tests/Generators/GoogleGeneratorTest.php @@ -19,4 +19,20 @@ protected function linkMethodName(): string { return 'google'; } + + /** @test */ + public function it_correctly_generates_all_day_events_by_days(): void + { + $this->assertMatchesSnapshot( + $this->generator()->generate($this->createAllDayEventMultipleDaysWithTimezoneLink()) + ); + } + + /** @test */ + public function it_correctly_generates_all_day_events_by_dates(): void + { + $this->assertMatchesSnapshot( + $this->generator()->generate($this->createEventMultipleDaysViaStartEndWithTimezoneLink()) + ); + } } diff --git a/tests/Generators/IcsGeneratorTest.php b/tests/Generators/IcsGeneratorTest.php index 9c3b481..11e2431 100644 --- a/tests/Generators/IcsGeneratorTest.php +++ b/tests/Generators/IcsGeneratorTest.php @@ -53,4 +53,20 @@ public function it_has_a_product_dtstamp(): void $this->generator(['DTSTAMP' => '20180201T090000Z'])->generate($this->createShortEventLink()) ); } + + /** @test */ + public function it_correctly_generates_all_day_events_by_days(): void + { + $this->assertMatchesSnapshot( + $this->generator()->generate($this->createAllDayEventMultipleDaysWithTimezoneLink()) + ); + } + + /** @test */ + public function it_correctly_generates_all_day_events_by_dates(): void + { + $this->assertMatchesSnapshot( + $this->generator()->generate($this->createEventMultipleDaysViaStartEndWithTimezoneLink()) + ); + } } diff --git a/tests/Generators/__snapshots__/GoogleGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt b/tests/Generators/__snapshots__/GoogleGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt new file mode 100644 index 0000000..2feec82 --- /dev/null +++ b/tests/Generators/__snapshots__/GoogleGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt @@ -0,0 +1 @@ +https://calendar.google.com/calendar/render?action=TEMPLATE&dates=20240125/20240131&ctz=UTC&text=All+day+bugs&details=Testing+all+day \ No newline at end of file diff --git a/tests/Generators/__snapshots__/GoogleGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt b/tests/Generators/__snapshots__/GoogleGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt new file mode 100644 index 0000000..3f414ee --- /dev/null +++ b/tests/Generators/__snapshots__/GoogleGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt @@ -0,0 +1 @@ +https://calendar.google.com/calendar/render?action=TEMPLATE&dates=20240125/20240130&ctz=UTC&text=All+day+bugs&details=Testing+all+day \ No newline at end of file diff --git a/tests/Generators/__snapshots__/IcsGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt b/tests/Generators/__snapshots__/IcsGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt new file mode 100644 index 0000000..28af5d4 --- /dev/null +++ b/tests/Generators/__snapshots__/IcsGeneratorTest__it_correctly_generates_all_day_events_by_dates__1.txt @@ -0,0 +1,12 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:Spatie calendar-links +BEGIN:VEVENT +UID:8fe3eececcd6b020db3b47ef55e7cf89 +SUMMARY:All day bugs +DTSTAMP:20240125 +DTSTART:20240125 +DURATION:P6D +DESCRIPTION:Testing all day +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/tests/Generators/__snapshots__/IcsGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt b/tests/Generators/__snapshots__/IcsGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt new file mode 100644 index 0000000..d230a35 --- /dev/null +++ b/tests/Generators/__snapshots__/IcsGeneratorTest__it_correctly_generates_all_day_events_by_days__1.txt @@ -0,0 +1,12 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:Spatie calendar-links +BEGIN:VEVENT +UID:b4be522f87b9894dadd2b9cd5479136b +SUMMARY:All day bugs +DTSTAMP:20240125 +DTSTART:20240125 +DURATION:P5D +DESCRIPTION:Testing all day +END:VEVENT +END:VCALENDAR \ No newline at end of file diff --git a/tests/TestCase.php b/tests/TestCase.php index 085d97b..138a70f 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -68,6 +68,33 @@ protected function createMultipleDaysAllDayEventLink(bool $immutable = false): L )->description($description)->address('Party Lane 1A, 1337 Funtown'); } + protected function createAllDayEventMultipleDaysWithTimezoneLink(bool $immutable = false): Link + { + $description = 'Testing all day'; + + $dateTimeClass = $immutable ? DateTimeImmutable::class : DateTime::class; + + return Link::createAllDay( + 'All day bugs', + $dateTimeClass::createFromFormat('Y-m-d', '2024-01-25', new DateTimeZone('Pacific/Wake'))->setTime(0,0), + 5 + )->description($description); + } + + protected function createEventMultipleDaysViaStartEndWithTimezoneLink(bool $immutable = false): Link + { + $description = 'Testing all day'; + + $dateTimeClass = $immutable ? DateTimeImmutable::class : DateTime::class; + + return Link::create( + 'All day bugs', + $dateTimeClass::createFromFormat('Y-m-d', '2024-01-25', new DateTimeZone('Pacific/Wake'))->setTime(0,0), + $dateTimeClass::createFromFormat('Y-m-d', '2024-01-30', new DateTimeZone('Pacific/Wake'))->setTime(0,0), + true, + )->description($description); + } + protected function createDescriptionIsHTMLcodeEventLink(bool $immutable = false): Link { $description = 'With balloons, clowns and stuff