Framework: Updating carbon field reverts to current date and time if there is no change in the field

Created on 24 Jan 2018  ·  17Comments  ·  Source: laravel/framework

  • Laravel Version: 5.5.28
  • PHP Version: 7.2

Description:

So I have a carbon field on my model called start_time

protected $dates = [
    'created_at',
    'updated_at',
    'deleted_at',
    'start_time'
];

And it all works well when creating a model or updating a model with a new start_time, but when I update the model without start_time changed, it changes it to the current date.

I have checked the input many times over and I am definitely sending the correct date to the update method, but if it's the same date and time then the model update changes it to the current date and time.

I have even resorted to sending the following to the update method to confirm that the issue is with the field being unchanged:
'start_time' => Carbon::createFromFormat('Y-m-d H:i', request('start_time'))->addMinutes(1)

By adding a minute to it it saves correctly.

Steps To Reproduce:

Have a field set as a carbon field in the model $dates variable.
do a Model->update() with the field unchanged.
The field reverts to current time

I believe this is the same issue as #22498

Most helpful comment

Thanks @Dewbud
Switching from timestamp() to dateTime() fixes this behavior

All 17 comments

As a workaround until the bug is fixedI have put this line before my actual update statement:
$model->update(['start_time' => Carbon::now()]);
Which then means my actual update works since start_time is now going to be dirty. I think the fact this workaround works further confirms the issue is as I explained above.

I'm also seeing this, no idea what's causing it.

@DeanStr your work around works, but I went with subtracting a second since my dates revolved around midnight.

I logged out the models date field from the controller and see the same thing DeanStr described.

```
// Without updating
[2018-01-26 20:32:29] local.DEBUG: 2018-01-02 04:59:59 // before save
[2018-01-26 20:32:29] local.DEBUG: 2018-01-26 20:32:29 // after save

// With updating
[2018-01-26 20:35:30] local.DEBUG: 2018-01-02 04:59:59 // before save
[2018-01-26 20:35:30] local.DEBUG: 2018-01-02 04:59:58 // after save
````

Can't replicate this.

Just out of curiosity do you have on update CURRENT_TIMESTAMP set for your datetime fields?

@arjasco
Didn't think I was but apparently the ->timestamp() migration defaults to CURRENT_TIMESTAMP even without ->useCurrent()

Checking my database properties after migration shows this.

Switching from timestamp() to dateTime() fixes this behavior

@DeanStr are you using timestamp() in your migration?

@Dewbud yes, also using timestamp()

So is this a bug with the timestamp() migration or just that it defaults to the current time is missing from the documentation?

Glad you guys figured it out. 👍

Are you running MySQL? and if so is it version >= 5.7.
See the Database section here on the older upgrade docs for 5.3 - https://laravel.com/docs/5.3/upgrade

Maybe this is apart of your problem.

I always have nullable timestamps and manage them myself if they should always have a value, I can't even run a migration with ->timestamp without it complaining i need a default value.

Locally I use sqlite, I guess that explains the no errors on migrations, however the issue is still present locally as well.

Production is mysql 5.7, however I've turned off strict mode due to another issue, but even before that I dont believe I had any issues when migrating.

In any case, I'd say there is a bug here in that:

  1. Defaulting to current_timestamp isnt expected behaviour
  2. Even with the default, it shouldnt revert to the default if I'm updating a model with a value set for the field.

@Dewbud @DeanStr ->timestamp() doesn't look like forcing CURRENT_TIMESTAMP without ->useCurrent()

You can take a look (MySqlGrammar.php line 638)

protected function typeTimestamp(Fluent $column)
{
    $columnType = $column->precision ? "timestamp($column->precision)" : 'timestamp';

    return $column->useCurrent ? "$columnType default CURRENT_TIMESTAMP" : $columnType;
}

I've tried it both on [5.4] and [5.5] using sqlite and I can't reproduce the issue.

Steps taken

  1. add an attribute to $dates
$dates = ['test'];
  1. add timestamp field
$table->timestamp('test');
  1. create a Model with custom date attribute
$user = User::create([
    'name' => 'john',
    'email' => 'john@email',
    'password' => 'password',
    'test' => Carbon::parse('2 weeks ago')
]);

I even tried passing a string to test

  1. update the model without passing a value for the custom date attribute
$user->update(['name' => 'jane']);

and

$user->update();

Both didn't alter the test field. $user->test always came as I saved when it was created.
Maybe I missed something, but I'll suggest looking on the side of the DB.
Can you replicate on a fresh install ?
Or somewhere there's a something forcing the hands of your timestamps.

I actually have no idea whether CURRENT_TIMESTAMP is being set as a default value, I just went off what @Dewbud said, how can I check that?

Like I said though, this is happening locally on sqlite and in production on mysql, do I doubt its a db issue, and since it's only happening when the timestamp value isn't dirty when sent to update I assume it's something in the laravel framework.

I can't replicate this, updating the model without changing the field doesn't change the value in the database.

I've had this happen before. Sometimes MySQL (depending on its SQL_MODE — I think?) will implicitly add DEFAULT CURRENT_TIMESTAMP or something as well as on update CURRENT_TIMESTAMP to the first timestamp column in a table matching this criteria:

  1. Does not have a default value; and
  2. Is not nullable.

I always make my timestamp columns nullable to get around this.

Closing then since it's not an issue in the framework.

I have the same issue too. Not sure what caused it.

I currently need to specify timestamp nullable and default null to prevent it.

I have the same issue too.
My custom end_date field updated when I call
$coupon->save();
Coupon is my model, field end_date is not touched in my code

Same issue here. It is clearly due to on update CURRENT_TIMESTAMP. Manually setting that to none fixes the issue, but the problem is that every time I refresh the DB on my local it set to on update CURRENT_TIMESTAMP again. Even more annoyingly this happens on local only, not in production, so it has clearly something to do with the MySql version/mode (as @thecrypticace assumed). Most likely the mode since we have same version on local/production.

Thanks @Dewbud
Switching from timestamp() to dateTime() fixes this behavior

Was this page helpful?
0 / 5 - 0 ratings