framework: toArray uses incorrect timezone.

  • Laravel Version: 7.0
  • PHP Version: 7.3
  • Database Driver & Version: Mysql 5.6

Description:

When using the toArray() method on a modal, it does not show the timestamps in the correct timezone.

Steps To Reproduce:

  1. Set the app.php timezone to America/Toronto

  2. Insert some rows for any model you want

  3. Create a blade file with this code for the model:

     created_at: {{ $model->created_at }}
     <br><br>
     via array: {{ print_r($model->toArray(), true) }}
    

You can clearly see that $model->created_at is showing the correct date via the exact database value, and for whatever reason toArray is deciding to change it to UTC.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 19 (10 by maintainers)

Most upvoted comments

@viirre @taylorotwell I really think it is wrong. Carbon::toJSON() method will create a copy of current $date and set it to utc(), ignoring the app.timezone. So non-UTC dates will have a wrong output when toArray() or similar is called. It is really intentional?

A simple solution is modify how serializeDate() works by calling toISOString(true) instead of toJSON(). The true on toISOString(true) means “do not set timezone to UTF, keep as is”.

I know this issue is closed but I’m trying anyway with a similar problem caused by this change.

There are some cases where this change might cause issues for a lot of apps, ours included. Consider:

        $invoiceModel = Invoice::make(['number' => 'foobar', 'date' => '2020-03-01']);
        $customer->invoices()->updateOrCreate(
            ['number' => $invoiceModel->number],
            $invoiceModel->attributesToArray()
        );

If the Invoice model has a date cast for the the date attribute, the date will be saved as 2020-02-29 instead of the correct 2020-03-01 because our timezone is set to Europe/Stockholm and not UTC.

The solution is to either set the timezone to UTC (which of course is not an option), set the cast as date:y-m-d or use the suggested upgrade guide solution of declaring our own serializedDate method on the model.

Is this really intended behaviour forattributesToArray/toArray regarding the date cast?

@markking79

This issue is tricky to understand, I tried to explain as best I could in the post https://github.com/laravel/framework/issues/31722#issuecomment-1131875330 above and more details in #42447

  1. We normally insert and read datetimes with the assumption that they are all in the same timezones as the system, in your case -04:00.
  2. The tricky bit about 2022-04-05T11:52:53.000000Z is that it is a datetime + zone, i.e. UTC+0
  3. Since you are on -4, after filling, created_at has to show 07:52:53, and we can see that serializeDate did not correct that

Don’t get me wrong. My concern is that this issue is hard to understand, so making it clear what does not work will better help everyone who reads this.

I was able to solve by adding the following method to my model…

use DateTimeInterface;
...
...

    protected function serializeDate(DateTimeInterface $date)
    {
        return \Carbon\Carbon::instance($date)->toISOString(true);
    }