Repository: getsentry/sentry-laravel (php)

Issue:
[Cron Monitoring] DateTimeZone Argument Error
### How do you use Sentry?

Sentry SaaS (sentry.io)

### SDK version

3.8.1

### Steps to reproduce

We recently updated from Sentry version 3.3.3 to 3.8.1, and when we did our Cron monitor started throwing errors. We aren't doing anything fancy, but here's our command:
```
$schedule->command(LogScheduledTasksRunning::class)->everyMinute()->sentryMonitor();
```

### Expected result

Using the old version of the SDK had no issue running the `sentryMonitor()` command without issue.

We've found that passing `sentryMonitor(updateMonitorConfig: false)` fixes the issue.

### Actual result

This error is thrown:
```
TypeError 

Sentry\MonitorConfig::__construct(): Argument #4 ($timezone) must be of type ?string, DateTimeZone given, called in /var/www/html/api/vendor/sentry/sentry-laravel/src/Sentry/Laravel/Features/ConsoleIntegration.php on line 97

at vendor/sentry/sentry/src/MonitorConfig.php:29
   25▕      * @var string|null The timezone
   26▕      */
   27▕     private $timezone;
   28▕ 
➜  29▕     public function __construct(
   30▕         MonitorSchedule $schedule,
   31▕         ?int $checkinMargin = null,
   32▕         ?int $maxRuntime = null,
   33▕         ?string $timezone = null

   +25 vendor frames 
26  artisan:35
   Illuminate\Foundation\Console\Kernel::handle()
```

Pull request:
Fix `DateTimeZone` not properly converted to a string
Fixes #782.

Patch:
diff --git a/src/Sentry/Laravel/Features/ConsoleIntegration.php b/src/Sentry/Laravel/Features/ConsoleIntegration.php
index 80850ae..49ab982 100644
--- a/src/Sentry/Laravel/Features/ConsoleIntegration.php
+++ b/src/Sentry/Laravel/Features/ConsoleIntegration.php
@@ -2,6 +2,7 @@
 
 namespace Sentry\Laravel\Features;
 
+use DateTimeZone;
 use Illuminate\Console\Application as ConsoleApplication;
 use Illuminate\Console\Scheduling\Event as SchedulingEvent;
 use Illuminate\Contracts\Cache\Factory as Cache;
@@ -90,11 +91,17 @@ class ConsoleIntegration extends Feature
         $checkIn = $this->createCheckIn($checkInSlug, CheckInStatus::inProgress());
 
         if ($updateMonitorConfig || $slug === null) {
+            $timezone = $scheduled->timezone;
+
+            if ($timezone instanceof DateTimeZone) {
+                $timezone = $timezone->getName();
+            }
+
             $checkIn->setMonitorConfig(new MonitorConfig(
                 MonitorSchedule::crontab($scheduled->getExpression()),
                 $checkInMargin,
                 $maxRuntime,
-                $scheduled->timezone
+                $timezone
             ));
         }
 


Test patch:
diff --git a/test/Sentry/Features/ConsoleIntegrationTest.php b/test/Sentry/Features/ConsoleIntegrationTest.php
index 6a5c25c..5c8c5b9 100644
--- a/test/Sentry/Features/ConsoleIntegrationTest.php
+++ b/test/Sentry/Features/ConsoleIntegrationTest.php
@@ -2,6 +2,7 @@
 
 namespace Sentry\Features;
 
+use DateTimeZone;
 use Illuminate\Console\Scheduling\Schedule;
 use RuntimeException;
 use Sentry\Laravel\Tests\TestCase;
@@ -27,6 +28,34 @@ class ConsoleIntegrationTest extends TestCase
         $this->assertEquals('test-monitor', $finishCheckInEvent->getCheckIn()->getMonitorSlug());
     }
 
+    /**
+     * When a timezone was defined on a command this would fail with:
+     * Sentry\MonitorConfig::__construct(): Argument #4 ($timezone) must be of type ?string, DateTimeZone given
+     * This test ensures that the timezone is properly converted to a string as expected.
+     */
+    public function testScheduleMacroWithTimeZone(): void
+    {
+        $expectedTimezone = 'UTC';
+
+        /** @var Event $scheduledEvent */
+        $scheduledEvent = $this->getScheduler()
+            ->call(function () {})
+            ->timezone(new DateTimeZone($expectedTimezone))
+            ->sentryMonitor('test-timezone-monitor');
+
+        $scheduledEvent->run($this->app);
+
+        // We expect a total of 2 events to be sent to Sentry:
+        // 1. The start check-in event
+        // 2. The finish check-in event
+        $this->assertEquals(2, $this->getEventsCount());
+
+        $finishCheckInEvent = $this->getLastEvent();
+
+        $this->assertNotNull($finishCheckInEvent->getCheckIn());
+        $this->assertEquals($expectedTimezone, $finishCheckInEvent->getCheckIn()->getMonitorConfig()->getTimezone());
+    }
+
     public function testScheduleMacroAutomaticSlug(): void
     {
         /** @var Event $scheduledEvent */