Framework: Eloquent's casted 'date' field disrespects Carbon instance's timezone when saving to database.

Created on 29 Aug 2016  路  7Comments  路  Source: laravel/framework

I faced a problem with saving date fields with Eloquent - it removed the timezone from Carbon instance and saved it in the timezone of the App.

So here my full note: http://lessthan12ms.com/save-all-dates-in-utc-for-multi-timezone-apps/

And to save time this is an example here:

<?php
dump(config('app.timezone'));
// OUTPUT: "UTC"

// We create a Carbon instance with timezone set to "GMT+3"
$date_in_local_timezone = Carbon::now('GMT+3');
// Let's  see the date we had initially
dump($date->toIso8601String())
// OUTPUT: "2015-01-01T12:00:00+0300"

// We then save it as "date" field
$model->some_date = $date_in_local_timezone;
$model->save();

// And then read it back from the model
dump($model->fresh()->some_date->toIso8601String());
// OUTPUT: "2015-01-01T12:00:00+0000"

As you can see we have the same datetime string but with timezone stripped.
I have my solution in the full note mentioned above.

Is it something to make a PR for or am I missing something?

Most helpful comment

I got your point about UTC default timezone.
But If I pass Carbon instsance with timezone - why Eloquent does not correctly transform it to UTC?
I don't mind storing in UTC, but it should convert my timezone to UTC before saving.
Why corrupt data stripping given timezone without conversion?

P.s. It looks like you should convert all your dates to UTC manually before saving it to Eloquent's model because Eloquent does not do that. I think it worth mentioning in the docs if that is desired.

All 7 comments

This is intended. We only want to save to the db as UTC, and then you convert it on the other side.

But I do pass Carbon instance with timezone but Eloquent discards it and saves as if I timezone is always UTC. It looks like not normal, no?
So the original date
2015-01-01T12:00:00+0300
should become
2015-01-01T09:00:00+0000
but became
2015-01-01T12:00:00+0000

Is it ok?

Yes, that's correct. The timezone doesn't reflect how it's saved in the db. That's always UTC, then you need to convert it again when you get it out.

I got your point about UTC default timezone.
But If I pass Carbon instsance with timezone - why Eloquent does not correctly transform it to UTC?
I don't mind storing in UTC, but it should convert my timezone to UTC before saving.
Why corrupt data stripping given timezone without conversion?

P.s. It looks like you should convert all your dates to UTC manually before saving it to Eloquent's model because Eloquent does not do that. I think it worth mentioning in the docs if that is desired.

I agree. Why is the timezone supplied simply truncated and ignored when saving it to the database instead of being correctly utilized to create an accurate version of the date & time in UTC?

I propose the following one line fix to the problem, in Illuminate\Database\Eloquent\Model.php line 2904:

    /**
     * Convert a DateTime to a storable string.
     *
     * @param  \DateTime|int  $value
     * @return string
     */
    public function fromDateTime($value)
    {
        $format = $this->getDateFormat();

        $value = $this->asDateTime($value);

        // ensure that if the datetime or carbon object we were given contained a timezone, we convert to the app timezone (instead of stripping it) before outputting a storable string
        return $value->setTimeZone(config('app.timezone'))->format($format);
    }

A notable good thing: Laravel is plenty of "_ways of doing_", it demonstrates as many aspects were well engineered matching the most common / frequent web app requirements and use cases

A notable bad thing: I think this is a "_way of NOT doing_" which carries many uncertainties for novice Laravel developers , an artisan needs everything spoon-fed to concentrate on his app business logic

Was this page helpful?
0 / 5 - 0 ratings