briannesbitt / Carbon

A simple PHP API extension for DateTime.
https://carbon.nesbot.com/
MIT License
16.59k stars 1.28k forks source link

Unable to add unit array error since updating to v3 #2976

Closed sts-ryan-holton closed 8 months ago

sts-ryan-holton commented 8 months ago

Hello,

I encountered an issue with the following code within my recently upgraded Laravel 11 project that was previously using the latest version of Carbon PHP v2, now, using the latest of v3 I'm experiencing an intermittent problem with some code

/**
 * Get charts
 */
protected function getCharts($monitor, $period = 24, $mode = 'average', $device = 'desktop')
{
    // user
    $user = Auth::user();

    /*
    * NOTE: the reason we do addHours($diff) is so that we don't miss
    * any data available to a user, in reality this hour difference could
    * be less, such as 12 hours.
    */

    // get UK time since that's the time data is saved
    $nowUK = Carbon::now();

    // get the user's local time, in their timezone
    $nowLocal = Carbon::now()->setTimezone($user->timezone)->toDateTimeString();

    // calculate the difference in hours so we can ensure a 24 hours period of data
    $diff = $nowUK->diffInHours($nowLocal);

    $from = Carbon::now()->setTimezone($user->timezone)->subHours($period)->toDateTimeString();
    $to = Carbon::now()->setTimezone($user->timezone)->addHours($diff)->toDateTimeString();

    $checkDateFormat = $period > 48 ? '%Y-%m-%d' : '%Y-%m-%d %H:00:00';
    $model = $period > 48 ? UptimeDailySummaryStat::class : UptimeHourlySummaryStat::class;

    // get the response times for past X hours
    $responseTimes = $model::where('user_id', Auth::id())
        ->where('monitor_id', $monitor['id'])
        ->where('grouped_at', '>=', $from)
        ->where('grouped_at', '<=', $to)
        ->select(
            DB::raw('SUM(total_checks_performed) AS total_events'),
            DB::raw('SUM(total_successful_checks_performed) AS up_events'),
            DB::raw('SUM(avg_response_duration) AS response_time'),
            DB::raw("DATE_FORMAT(grouped_at, '$checkDateFormat') AS group_date"),
        )
        ->groupBy('group_date')
        ->orderBy('group_date', 'asc')
        ->get();

    if (! $responseTimes) {
        return [];
    }

    $timeline = $this->getUptimeTimeline($monitor['id'], $responseTimes, $from, $to, $device, $period, $user);

    if (! $timeline) {
        return [];
    }

    $charts = [
        'uptime' => $timeline,
        'response' => [
            'labels' => [],
            'data' => [],
        ],
    ];

    foreach ($responseTimes as $uptime) {
        array_push($charts['response']['labels'], Carbon::parse($uptime['group_date'])->setTimezone($user->timezone)->toISOString());
        array_push($charts['response']['data'], intval($uptime['response_time']));
    }

    return $charts;
}

Carbon version: 3.1.1

PHP version: 8.3.4

I expected to get:

Ideally not an error since coming from v2 there's no issue and i can't find an upgrade guide for carbon v3

But I actually get:

[2024-03-25 11:57:59] local.ERROR: Unable to add unit array (
  0 => 'hour',
  1 => -3.08025E-5,
  2 => NULL,
) {"userId":142,"exception":"[object] (Carbon\\Exceptions\\UnitException(code: 0): Unable to add unit array (
  0 => 'hour',
  1 => -3.08025E-5,
  2 => NULL,
) at /var/www/html/vendor/nesbot/carbon/src/Carbon/Traits/Units.php:336)
[stacktrace]
#0 /var/www/html/vendor/nesbot/carbon/src/Carbon/Traits/Date.php(2698): Carbon\\Carbon->addUnit()
#1 /var/www/html/app/Http/Controllers/Account/MonitorController.php(109): Carbon\\Carbon->__call()
#2 /var/www/html/app/Http/Controllers/Account/MonitorController.php(590): App\\Http\\Controllers\\Account\\MonitorController->getCharts()
#3 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\Account\\MonitorController->show()
#4 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(43): Illuminate\\Routing\\Controller->callAction()
#5 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php(260): Illuminate\\Routing\\ControllerDispatcher->dispatch()
#6 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php(206): Illuminate\\Routing\\Route->runController()
#7 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php(806): Illuminate\\Routing\\Route->run()
#8 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()
#9 /var/www/html/app/Http/Middleware/ValidateAccountOwningModel.php(35): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#10 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): App\\Http\\Middleware\\ValidateAccountOwningModel->handle()
#11 /var/www/html/app/Http/Middleware/OnboardingJourney.php(48): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#12 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): App\\Http\\Middleware\\OnboardingJourney->handle()
#13 /var/www/html/vendor/laravel/framework/src/Illuminate/Auth/Middleware/EnsureEmailIsVerified.php(41): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#14 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Auth\\Middleware\\EnsureEmailIsVerified->handle()
#15 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(50): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#16 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle()
#17 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(159): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#18 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(90): Illuminate\\Routing\\Middleware\\ThrottleRequests->handleRequest()
#19 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Routing\\Middleware\\ThrottleRequests->handle()
#20 /var/www/html/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php(64): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#21 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Illuminate\\Auth\\Middleware\\Authenticate->handle()
#22 /var/www/html/vendor/laravel/sanctum/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php(25): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#23 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(144): Laravel\\Sanctum\\Http\\Middleware\\EnsureFrontendRequestsAreStateful->Laravel\\Sanctum\\Http\\Middleware\\{closure}()
#24 /var/www/html/vendor/laravel/sanctum/src/Http/Middleware/AuthenticateSession.php(66): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#25 /var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183): Laravel\\Sanctum\\Http\\Middleware\\AuthenticateSession->handle()
#26 /var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php(88): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}

Thanks!

kylekatarnls commented 8 months ago

Hello, it's difficult to see what's the issue since I don't know what line matches the 109 of your MonitorController.php file.

Can you make a minimum code sample free of DB/application context so that I can run it as is on my side and reproduce your error?

sts-ryan-holton commented 8 months ago

Line 109 is:

$to = Carbon::now()->setTimezone($user->timezone)->addHours($diff)->toDateTimeString();

I figured it might be something to do with the breaking change to how diffIn* methods work. So I've wrapped these in the (int) checks

kylekatarnls commented 8 months ago

It's weird because echo Carbon::now()->addHours(0.5)->toDateTimeString(); is working on both Carbon 2 and 3. So float vs. int should not break it.

If you can provide a reproductible code chunk, I would re-open the issue.