Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DateTime does not equal itself after unserialization

As you can see below, after serializing and unserializing, you get DateTime instances that are supposedly different:

scala> import org.joda.time.DateTime
import org.joda.time.DateTime

scala> val a = new DateTime()
a: org.joda.time.DateTime = 2014-01-08T19:00:08.883+02:00

scala> val b = DateTime.parse(a.toString())
b: org.joda.time.DateTime = 2014-01-08T19:00:08.883+02:00

scala> a == b
res0: Boolean = false

As per AbstractInstant's javadoc, equals "compares this object with the specified object for equality based on the millisecond instant, chronology and time zone." So this shouldn't be happening right? What am I missing?

like image 703
Dominykas Mostauskis Avatar asked Jan 08 '14 17:01

Dominykas Mostauskis


3 Answers

Here is the only right answer, found by own testing:

DateTime a = new DateTime(); // uses default time zone
System.out.println(a); // 2014-01-08T19:38:00.696+01:00

DateTime b = DateTime.parse(a.toString());
System.out.println(b); // 2014-01-08T19:38:00.696+01:00

System.out.println(a.getChronology()); // ISOChronology[Europe/Berlin]
System.out.println(b.getChronology()); // ISOChronology[+01:00]
System.out.println(a.equals(b)); // false!!!

I have assumed that in Scala the comparison by == indeed means comparison by equals() as @aris1348880 has stated in his comment, therefore I have replaced in the translation to java code the operator correspondingly.

So the cause of failed equals()-comparison is obvious: It is the time zone id which is not printed correctly in the toString()-method of DateTime object a. I consider it as a bug in JodaTime because toString() should always print the whole state of an immutable value object. By the way, in this detail old java.util.Date is even worse! Well, as work around you can use the format engine of JodaTime in order to print out correctly.

System.out.println(
    DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'['ZZZ']'").print(a));
// Output: 2014-01-08T19:38:00.696[Europe/Berlin]

To make the behaviour more clear:

The DateTime-constructor uses the default time zone (in my test Europe/Berlin). Its toString()-method prints the offset instead, not the real time zone (that is the problem).

The DateTime-parse()-method uses according to the JodaTime documentation ISODateTimeFormat#dateTimeParser() which again can only parse offsets, but in this code it was also feeded with just offset information, not with the real time zone, otherwise the parsing would have stopped with an exception.

In both cases instances of DateTime are created differently that is with different time zones / offsets and hence different chronologies. So the equals()-result is on first glance astonishing but understandable.

like image 173
Meno Hochschild Avatar answered Nov 15 '22 03:11

Meno Hochschild


I opened an issue on GitHub, here is the author's response:

A long time ago I chose the toString format of DateTime, and I omitted to include the full state of the value type. As a result, two objects look like they are equal (by toString) but they are not. That was a mistake as it causes this kind of confusion. Unfortunately, it cannot be corrected.

The parsing behaviour is also unfortunate, as it focuses too much on the offset and not enough on the time-zone (as ISO-8601 does not handle time zone IDs). Again, its too late to make changes here.

like image 5
Dominykas Mostauskis Avatar answered Nov 15 '22 04:11

Dominykas Mostauskis


I haven't org.joda.time.DateTime but I just tried a test with java.util.Date.

You should try this:

val a = new DateTime()
val b = new DateTime(a.getMillis())
a == b

I also tried this:

import org.joda.time.DateTime;

public class TestDateTime {

    public static void main(String[] args) {
        DateTime a = new DateTime();
        System.out.println(a.toString());
        DateTime b = DateTime.parse(a.toString());
        System.out.println(b.toString());
        System.out.println(a.equals(b));
    }
}

and this is the output:

2014-01-08T19:05:52.182+01:00
2014-01-08T19:05:52.182+01:00
false

And the problem is that equals of DateTime fails because different time zone. I think it's wrong to assume equivalence of two DateTime instances created in such different way.

When you serialize and deserialize a java.util.Date the long (the wrapped value) is passed not a String.

like image 3
Aris2World Avatar answered Nov 15 '22 04:11

Aris2World