diff --git a/src/Events.php b/src/Events.php index 5e19268..352a48b 100644 --- a/src/Events.php +++ b/src/Events.php @@ -4,6 +4,7 @@ use Carbon\CarbonImmutable; use Carbon\CarbonInterface; +use DateTimeZone; use Exception; use Illuminate\Pagination\Paginator; use Illuminate\Support\Collection; @@ -43,7 +44,7 @@ class Events public static function defaultTimezone(): string { - return static::setting('timezone'); + return static::resolveTimezone(); } public static function fromCollection(string $handle): self @@ -56,6 +57,17 @@ public static function fromEntry(string $id): self return new static(new Parameters(['event' => $id])); } + public static function resolveTimezone(mixed $timezone = null): string + { + return collect([ + $timezone, + static::setting('timezone'), + config('statamic.system.display_timezone'), + config('app.timezone'), + 'UTC', + ])->first(fn (mixed $candidate) => static::isValidTimezone($candidate)); + } + public function __construct(Parameters $params) { throw_if( @@ -71,7 +83,7 @@ public function __construct(Parameters $params) ->offset(offset: $params->int('offset')) ->pagination(page: Paginator::resolveCurrentPage(), perPage: $params->int('paginate')) ->sort($params->get('sort', 'asc')) - ->timezone(timezone: $params->get('timezone', static::defaultTimezone())); + ->timezone(timezone: $params->get('timezone')); } public static function setting(string $key, $default = null): mixed @@ -172,9 +184,9 @@ public function terms(string|array $terms): self return $this; } - public function timezone(string $timezone): self + public function timezone(?string $timezone = null): self { - $this->timezone = $timezone; + $this->timezone = static::resolveTimezone($timezone); return $this; } @@ -195,6 +207,21 @@ public function upcoming(int $limit = 1): EntryCollection|LengthAwarePaginator ); } + private static function isValidTimezone(mixed $timezone): bool + { + if (! is_string($timezone) || ! filled($timezone)) { + return false; + } + + try { + new DateTimeZone($timezone); + } catch (Exception) { + return false; + } + + return true; + } + private function output(callable $type, string|CarbonInterface $from): EntryCollection|LengthAwarePaginator { $occurrences = $this->entries()->occurrences(generator: $type, from: $from); diff --git a/src/Tags/Events.php b/src/Tags/Events.php index 2f8fb2c..09c0b0f 100755 --- a/src/Tags/Events.php +++ b/src/Tags/Events.php @@ -38,7 +38,7 @@ public function calendar(): Collection $month = $this->params->get('month', now()->englishMonth); $year = $this->params->get('year', now()->year); - $timezone = $this->params->get('timezone', Generator::defaultTimezone()); + $timezone = Generator::resolveTimezone($this->params->get('timezone')); $from = parse_date($month.' '.$year)->shiftTimezone($timezone)->startOfMonth()->startOfWeek(); $to = parse_date($month.' '.$year)->shiftTimezone($timezone)->endOfMonth()->endOfWeek(); diff --git a/tests/Tags/EventsTest.php b/tests/Tags/EventsTest.php index 414d5d6..f897772 100755 --- a/tests/Tags/EventsTest.php +++ b/tests/Tags/EventsTest.php @@ -8,6 +8,7 @@ use Statamic\Facades\Site as SiteFacade; use Statamic\Sites\Site; use Statamic\Support\Arr; +use TransformStudios\Events\Events; use TransformStudios\Events\Tags\Events as EventsTag; beforeEach(function () { @@ -663,6 +664,42 @@ ->first()->start->timezone->getName()->toBe('America/Vancouver'); }); +it('falls back to the default timezone when the timezone param is empty', function () { + Carbon::setTestNow(now()->setTimeFromTimeString('10:00')); + + $this->tag + ->setContext([]) + ->setParameters([ + 'collection' => 'events', + 'from' => Carbon::now()->subDay(), + 'timezone' => '', + 'to' => Carbon::now()->addDays(2), + ]); + + $occurrences = $this->tag->between(); + + expect($occurrences)->toHaveCount(1) + ->first()->start->timezone->getName()->toBe(Events::defaultTimezone()); +}); + +it('falls back to the default timezone when the timezone param is invalid', function () { + Carbon::setTestNow(now()->setTimeFromTimeString('10:00')); + + $this->tag + ->setContext([]) + ->setParameters([ + 'collection' => 'events', + 'from' => Carbon::now()->subDay(), + 'timezone' => 'not-a-real-timezone', + 'to' => Carbon::now()->addDays(2), + ]); + + $occurrences = $this->tag->between(); + + expect($occurrences)->toHaveCount(1) + ->first()->start->timezone->getName()->toBe(Events::defaultTimezone()); +}); + it('sets "spanning"', function () { Carbon::setTestNow(now()->setTimeFromTimeString('10:00'));