Bug using equals method to compare Dates

Hello all,

I just ran into a rather nasty bug/gotcha with comparing Dates using the equals comparison that frankly shocked me. As an example, you can reproduce with the following test, running against the standard test dataset. (I was running against Core 2.5.x)

Encounter encounter = Context.getEncounterService().getEncounter(3);
Date encounterDate = new  Date(encounter.getEncounterDatetime().getTime());
Assert.assertTrue(encounterDate.equals(encounter.getEncounterDatetime()));  // PASSES
Assert.assertTrue(encounter.getEncounterDatetime().equals(encounterDate));  // FAILS

Note that the first assertion passes, and the second doesn’t.

What’s happening here is that when Hibernate hydrates the encounter date, it’s getting hydrated as a Timestamp, but the encounterDate I create in the second line is a pure Date… and evidently Timestamp equals is not symmetric with the base Date equal:

https://docs.oracle.com/javase/8/docs/api/java/sql/Timestamp.html#equals-java.lang.Object-

The bottom line is simply to not use equals, but to use something like Apache DateUtils.isSameInstant to compare dates or a handy OpenMRS Util method for doing this: openmrs-core/OpenmrsUtil.java at master · openmrs/openmrs-core · GitHub

So the fix is easy, but I would think there are numerous places in the code where were are using the equals method and expecting it to work… but maybe it’s just me and everybody knew about this… :slight_smile: .

I’m also wondering why Hibernate instantiates encounterDatetime as a Timestamp instead of a Date.

fyi @mseaton @dkayiwa @mksd @burke @ibacher

Take care, Mark

3 Likes

Because the java.util.Date type (milliseconds precision) is not precise enough to handle the Timestamp value (nanoseconds precision). If you want to force Hibernate to use a Date, then you would need to add @Type(type="date") here: https://github.com/openmrs/openmrs-core/blob/2.6.0/api/src/main/java/org/openmrs/Encounter.java#L68