Be part of JetBrains PHPverse 2026 on June 9 โ€“ a free online event bringing PHP devs worldwide together.

iraklisg's avatar

Casting model's attributes to date does not respect timezone when formated

I have created a fresh Laravel 7 application.

php artisan --version
Laravel Framework 7.9.2

The application is configured to my local timezone:

config/app.php


...
'timezone' => 'Europe/Athens',
...

The application is also connected to a MySQL database server (version 5.7)

Now I have a simple User model as follows:

class User extends Model
{
    protected $fillable = ['name', 'birthdate'];

    protected $dates = ['birthdate'];
}

Note I use a date mutator in order to convert the birthdate column to Carbon instance.

The model is associated with a users table that have been created using migrations, as shown below:

public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name', 40);
            $table->date('birthdate')->nullable();
        });
    }

When I save a new model in the database, the birthdate column is created in the expected Y-m-d H:i:s format, e.g

id | name       | birthdate
--------------------------------------------------
1  | John Doe | 2000-05-06 15:31:59

Now when I try to serialize the model I get the following response:

$user = User::find(1);
$user->toArray();

=>
  [
     "id" => 1,
     "name" => "John Doe",
     "birthdate" => "2020-05-06T12:31:59.000000Z"
   ]

Note that the bithdate is formatted as ISO-8601, according to the new date serialization introduced to Laravel 7. The Europe/Athens timezone is +3h compared to Zulu time

So far so good...

However If I try to use attribute casting to cast the date formated in "Y-m-d H:i:s" as follows

    protected $casts = [
        'birthdate' => 'date:Y-m-d H:i:s',
    ];

and then try to fetch the serialized model, I get the following:

$user = User::find(1);
$user->toArray();

=>
  [
     "id" => 1,
     "name" => "John Doe",
     "birthdate" => "2000-05-06 12:31:59", // <-- wrong time, it should be  2000-05-06 15:31:59
   ]

Note that if instead of using casts, I override the serializeDate method, as shown in documentation works as expected:

// In User model
    protected function serializeDate(DateTimeInterface $date)
    {
        return $date->format('Y-m-d H:i:s');
    }

// Then...
$user = User::find(1);
$user->toArray();

=>
  [
     "id" => 1,
     "name" => "John Doe",
     "birthdate" => " 2000-05-06 15:31:59", // <-- yay!
   ]

Why is this happening? Is this the expected behavior using $casts ?

0 likes
8 replies
MichalOravec's avatar

@iraklisg Just change your timezine here.

protected function serializeDate(DateTimeInterface $date)
{
    return $date->timezone('your timezone')->format('Y-m-d H:i:s');
}
2 likes
iraklisg's avatar

Thank you for your reply. However, I don't need to change the timezone here, since serializeDate seems to already take into account my timezone and formats the date as needed, that is 2000-05-06 15:31:59 which is the date stored in my database.

My question, in this awfully long post, is why $casts does not work the same way.

iraklisg's avatar

After digging a bit more into how to cast an attribute to date using a custom format, I noticed that I should have included the timezone information, as well. This can be done as follows:

    protected $casts = [
        'birthdate' => 'datetime',
    ];
// Output => 2020-05-06T12:31:59.000000Z  ๐Ÿ‘

// Or alternatively
    protected $casts = [
        'birthdate' => 'date:Y-m-d H:i:sT',
    ];
// Output => 2020-05-06 12:31:59Z  ๐Ÿ‘

// Or alternatively
    protected $casts = [
        'birthdate' => 'date:c',
    ];
// Output => 2020-05-06T12:31:59+00:00  ๐Ÿ‘
lancis's avatar

I have this same problem. In my case, my timezone is IST i.e GMT+5:30hr. The timezone info in the $casts do not work for me, I have to settle with the serializeDate method.

It's confusing.

dorqa95's avatar

In Laravel 9.51 I was able to format proper datetime in local timezone with casts like this:

protected $casts = [
    'birthdate' => 'custom_datetime:Y-m-d H-i-s',
];
1 like
gssj85's avatar

@dorqa95 I was using this solution but I just noticed that after the : nothing really matters.

'custom_datetime' = no effect 'custom_datetime:' = formats as Y-m-d H-i-s 'custom_datetime:'Y-m-d H:i:s = formats as Y-m-d H:i:s

Strange...

ulrichm's avatar

My problem was that I had the variable in protected $dates = [..] as well as in protected $casts = [..]

No matter how I formatted like the above examples, it didnยจt work.

BUT when I removed the var from $dates, it worked!

sedrak1987's avatar

protected $casts = [ 'created_at' => TimezoneAwareDateCast::class . ':America/New_York,Y-m-d H:i:s', 'updated_at' => TimezoneAwareDateCast::class . ':Europe/London,d/m/Y H:i', 'publish_date' => TimezoneAwareDateCast::class . ':UTC,Y-m-d', //'created_at' => TimezoneAwareDateCast::class,

// 'updated_at' => TimezoneAwareDateCast::class . ':,d/m/Y H:i', ];

namespace App\Casts;

use Carbon\Carbon; use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use Illuminate\Database\Eloquent\Model;

class TimezoneAwareDateCast implements CastsAttributes { protected $timezone; protected $format;

public function __construct($timezone = 'default', $format = 'Y-m-d H:i:s')
{
    $this->timezone = $this->resolveTimezone($timezone);
    $this->format = $format;
}

protected function resolveTimezone($timezone)
{
    if ($timezone === 'default' || empty($timezone)) {
        return config('app.timezone');
    }
    
    if ($timezone === 'user') {
        return auth()->user()?->timezone ?? config('app.timezone');
    }
    
    return $timezone;
}

public function get(Model $model, string $key, mixed $value, array $attributes): ?string
{
    if (is_null($value)) {
        return null;
    }

    return Carbon::parse($value)
        ->setTimezone($this->timezone)
        ->format($this->format);
}

public function set(Model $model, string $key, mixed $value, array $attributes): ?string
{
    if (is_null($value)) {
        return null;
    }

    return Carbon::parse($value, $this->timezone)
        ->utc()
        ->format('Y-m-d H:i:s');
}

}

Please or to participate in this conversation.