diff --git a/.github/workflows/gh-action-updater.yml b/.github/workflows/gh-action-updater.yml index 53be976..c7bc029 100644 --- a/.github/workflows/gh-action-updater.yml +++ b/.github/workflows/gh-action-updater.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.2 + - uses: actions/checkout@v7.0.1 with: # [Required] Access token with `workflow` scope. token: ${{ secrets.WORKFLOW_SECRET }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 97cf54c..81ec99a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,16 +17,16 @@ jobs: name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} - ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v6.0.2 + uses: actions/checkout@v7.0.1 - name: Cache dependencies - uses: actions/cache@v5.0.5 + uses: actions/cache@v6.1.0 with: path: ~/.composer/cache/files key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} - name: Setup PHP - uses: shivammathur/setup-php@2.37.1 + uses: shivammathur/setup-php@2.37.2 with: php-version: ${{ matrix.php }} extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick 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'));