DDC-1310: Datetime fields merge bug #1646

Closed
opened 2026-01-22 13:20:56 +01:00 by admin · 7 comments
Owner

Originally created by @doctrinebot on GitHub (Aug 1, 2011).

Originally assigned to: @beberlei on GitHub.

Jira issue originally created by user slaci:

Merge compares Datetime objects by ===; so if you assign a new datetime object to a datetime field (which contains the same date in the db already), then merge will issue an UPDATE on that field (because the objects hashes don't match for ===). The expected behavior is to assume it is unchanged, so no update.

Solution: Datetime objects should be compared by == (or by $dt->format('something') maybe)

Easy to reproduce:

<?php
/*** @Entity **/
class EgEntity {
  /*** @Id **/
  private $id;
  /**** @Column(type="datetime")
  private meeting;
  //other fields...

  public function setId($id) {
    $this->id = (int)$id;
  }

  public function setMeeting(Datetime $dt) {
    $this->meeting = $dt;
  }
}
$a = new EgEntity();
$a->setId(1);
$a->setMeeting(new Datetime('2011-08-01'));
$em->merge($a); //this will issue an UPDATE even if id 1 is 2011-08-01 already
?>
Originally created by @doctrinebot on GitHub (Aug 1, 2011). Originally assigned to: @beberlei on GitHub. Jira issue originally created by user slaci: Merge compares Datetime objects by ===; so if you assign a new datetime object to a datetime field (which contains the same date in the db already), then merge will issue an UPDATE on that field (because the objects hashes don't match for ===). The expected behavior is to assume it is unchanged, so no update. Solution: Datetime objects should be compared by == (or by $dt->format('something') maybe) Easy to reproduce: ``` <?php /*** @Entity **/ class EgEntity { /*** @Id **/ private $id; /**** @Column(type="datetime") private meeting; //other fields... public function setId($id) { $this->id = (int)$id; } public function setMeeting(Datetime $dt) { $this->meeting = $dt; } } $a = new EgEntity(); $a->setId(1); $a->setMeeting(new Datetime('2011-08-01')); $em->merge($a); //this will issue an UPDATE even if id 1 is 2011-08-01 already ?> ```
admin added the Bug label 2026-01-22 13:20:56 +01:00
admin closed this issue 2026-01-22 13:20:57 +01:00
Author
Owner

@doctrinebot commented on GitHub (Aug 6, 2011):

Comment created by @beberlei:

That is expected behavior, objects are compared by reference, otherwise we couldn't implement a generic comparison mechanism for all types based on objects and the code would get confusing and slow (this conversion/comparison loops are some of the most called code in Doctrine 2)

@doctrinebot commented on GitHub (Aug 6, 2011): Comment created by @beberlei: That is expected behavior, objects are compared by reference, otherwise we couldn't implement a generic comparison mechanism for all types based on objects and the code would get confusing and slow (this conversion/comparison loops are some of the most called code in Doctrine 2)
Author
Owner

@doctrinebot commented on GitHub (Aug 6, 2011):

Issue was closed with resolution "Invalid"

@doctrinebot commented on GitHub (Aug 6, 2011): Issue was closed with resolution "Invalid"
Author
Owner

@doctrinebot commented on GitHub (Jul 3, 2015):

Comment created by razmo:

Have you find a solution?

I use doctrine2 on symfony2 and have the following error when i try to merge a DateTime object:
"The class 'DateTime' was not found in the chain configured namespaces ...."

@doctrinebot commented on GitHub (Jul 3, 2015): Comment created by razmo: Have you find a solution? I use doctrine2 on symfony2 and have the following error when i try to merge a DateTime object: "The class 'DateTime' was not found in the chain configured namespaces ...."
Author
Owner

@doctrinebot commented on GitHub (Jul 3, 2015):

Comment created by slaci:

Hi Jamal.

No solution for my problem, but yours look different.
I tried my example class (fixed to a working one, and for symfony 2: https://gist.github.com/slaci/f49271638b77c30993b7) and this works:

    /****
     * @Route("/", name="homepage")
     */
    public function indexAction()
    {
        $eg = new \AppBundle\Entity\EgEntity();
        $eg->setId(1);
        $eg->setMeeting(new \DateTime('2015-07-03 10:00:00'));

        $em = $this->getDoctrine()->getManager();
        $em->merge($eg);
        $em->flush();
   }

This stuff runs without error, and always runs the update query (which was the topic of this issue many years ago and sad it's still not fixed).

You are using another DateTime class from a custom namespace, not the builtin \DateTime, or i don't know, but this should work when used correctly.

@doctrinebot commented on GitHub (Jul 3, 2015): Comment created by slaci: Hi Jamal. No solution for my problem, but yours look different. I tried my example class (fixed to a working one, and for symfony 2: https://gist.github.com/slaci/f49271638b77c30993b7) and this works: ``` /**** * @Route("/", name="homepage") */ public function indexAction() { $eg = new \AppBundle\Entity\EgEntity(); $eg->setId(1); $eg->setMeeting(new \DateTime('2015-07-03 10:00:00')); $em = $this->getDoctrine()->getManager(); $em->merge($eg); $em->flush(); } ``` This stuff runs without error, and always runs the update query (which was the topic of this issue many years ago and sad it's still not fixed). You are using another DateTime class from a custom namespace, not the builtin \DateTime, or i don't know, but this should work when used correctly.
Author
Owner

@s001dxp commented on GitHub (Jul 25, 2022):

This should be changed. It doesn't have to be a solution for all objects, but for DateTime object which are common types.

The UnitOfWork code could add something as simple as this:
if($actualValue instanceof \DateTime && $orgValue instanceof \DateTime) { if($actualValue->format('Y-m-d H:i:s') == $orgValue->format('Y-m-d H:i:s')) { continue; } }

@s001dxp commented on GitHub (Jul 25, 2022): This should be changed. It doesn't have to be a solution for all objects, but for DateTime object which are common types. The UnitOfWork code could add something as simple as this: ` if($actualValue instanceof \DateTime && $orgValue instanceof \DateTime) { if($actualValue->format('Y-m-d H:i:s') == $orgValue->format('Y-m-d H:i:s')) { continue; } }`
Author
Owner

@janklan commented on GitHub (May 5, 2023):

The UnitOfWork code could add something as simple as this: if($actualValue instanceof \DateTime && $orgValue instanceof \DateTime) { if($actualValue->format('Y-m-d H:i:s') == $orgValue->format('Y-m-d H:i:s')) { continue; } }

That wouldn't account for possible time zone changes. Better using $first->format('ce') === $second->format('ce'). Other than that, I agree. I just tripped over the same issue.

With that said, it's been 13 years since creating this - is it not bothering anyone enough?

@beberlei does your opinion/response from Aug 7, 2011 still hold? Would a datetime-specific check be that bad of an exception?

I had a quick stab at the snippet I wrote above and it seems to be working fine: https://3v4l.org/k6Obe

@janklan commented on GitHub (May 5, 2023): > The UnitOfWork code could add something as simple as this: ` if($actualValue instanceof \DateTime && $orgValue instanceof \DateTime) { if($actualValue->format('Y-m-d H:i:s') == $orgValue->format('Y-m-d H:i:s')) { continue; } }` That wouldn't account for possible time zone changes. Better using `$first->format('ce') === $second->format('ce')`. Other than that, I agree. I just tripped over the same issue. With that said, it's been 13 years since creating this - is it not bothering anyone enough? @beberlei does your opinion/response from Aug 7, 2011 still hold? Would a datetime-specific check be that bad of an exception? I had a quick stab at the snippet I wrote above and it seems to be working fine: https://3v4l.org/k6Obe
Author
Owner

@amici commented on GitHub (Sep 27, 2023):

It's bothering me, at least :), for a while now. I do also wonder why are not more people hit by this. Maybe because it's not so obvious when it happens, but still.

This is far from rare issue - each time you flush an entity with a DateTime, due to this, you know it would surely have something in the change set (even if nothing changed) and would thus trigger a DB update.

The DB update might not actually do anything (since the values in the DB remain the same), but the existing change set might also trigger other pieces - for example updating the timestamps/userstamps, so you end up with a real update on the database - at least because your timestamps keep updating (for no reason).

Anyway, any update to DB, even an "empty" one, comes with a cost.

@amici commented on GitHub (Sep 27, 2023): It's bothering me, at least :), for a while now. I do also wonder why are not more people hit by this. Maybe because it's not so obvious when it happens, but still. This is far from rare issue - each time you flush an entity with a DateTime, due to this, you know it would surely have something in the change set (even if nothing changed) and would thus trigger a DB update. The DB update might not actually do anything (since the values in the DB remain the same), but the existing change set might also trigger other pieces - for example updating the timestamps/userstamps, so you end up with a **real** update on the database - at least because your timestamps keep updating (for no reason). Anyway, any update to DB, even an "empty" one, comes with a cost.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#1646