Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Joda LocalDate - still an instant?

Tags:

java

jodatime

I'm looking for a proper serializable date-only class. Got a server in Central time zone, I want users in the Eastern to enter date as 2010-11-23, and users in Pacific to see it as 2010-11-23 (and vice versa).

java.util.Date is a time-sensitive instant, so it doesn't work for me. Not wanting to reinvent the wheel, I decided to give Joda Time a try. According to overview, LocalDate is a "class representing a local date without a time (no time zone)" - just what I need.

Unfortunately, it does not seem to be time zone independent. In database I have 2010-11-23. On server, I transform it to LocalDate with new LocalDate(java.sql.Date). date.toString() prints 2010-11-23.

After deserializing that on client, date.ToString() prints 2010-11-22 and date.getDayOfMonth() is 22. But date.toDateTimeAtStartOfDay() produces 2010-11-23T00:00:00.000-08:00.

Am I doing something wrong? Is there a proper date-only time zone insensitive class in Joda?

EDIT: In database I am using a timezone insensitive column (DATE in Postgres). Server is in the same time zone as the database, so it reads the date just fine. It is not an issue. I am looking for a Java type that once instantiated in one time zone, has the same civil (partial) date value in all time zones.

EDIT 2: On server everything is loaded properly and has UTC time zone. After investigation I think it is a bug in ISOChronology. On client it is unpacked as America/Los_Angeles. The reason is that serialization is done with:

class ISOChronology {
// ...
    private Object writeReplace() {
        return new Stub(getZone());
    }

    private static final class Stub implements Serializable {
        private static final long serialVersionUID = -6212696554273812441L;

        private transient DateTimeZone iZone;

        Stub(DateTimeZone zone) {
            iZone = zone;
        }

        private Object readResolve() {
            return ISOChronology.getInstance(iZone);
        }

        private void writeObject(ObjectOutputStream out) throws IOException {
            out.writeObject(iZone);
        }

        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
            iZone = (DateTimeZone)in.readObject();
        }
    }
}

Good luck serializing with a transient field.

like image 999
Konrad Garus Avatar asked Nov 24 '10 15:11

Konrad Garus


1 Answers

The trasient time zone in the stub class shown above is a bug. Amazing that it wasn't caught in tens of thousands of tests or 5 years of widespread usage. Bug report https://sourceforge.net/tracker/index.php?func=detail&aid=3117678&group_id=97367&atid=617889

The timezone should be UTC, as defined in the Javadoc - http://joda-time.sourceforge.net/apidocs/org/joda/time/LocalDate.html . Deserializing to get any other time zone will break the internal state of the class.

JSR-310 uses a much better internal design which doesn't suffer from this kind of problem.

like image 72
JodaStephen Avatar answered Nov 15 '22 02:11

JodaStephen