Slartibartfast
Slartibartfast

Reputation: 8805

Serializing Date in Java

I'm passing around some objects through web service and some of them contain java.sql.Date. Because Date doesn't have empty constructor it doesn't want to get serialized.

First part of a question is easy: what is the best way to pass a date between client and service?

Second part is bit trickier: Once I decide how to pass dates around, I can obviously declare date transient and make some wrapper class to pass dates as String or whatever, but how to apply same solution as transparently as possible to several classes that include Date?

(I have a hunch that DynamicProxy thingy might be a solution, but reading documentation on Sun's site wasn't very helpful, so if it really is something in that direction, some clarification would be appreciated)

Edit: I asked wrong question, sorry (some misunderstanding between me and coworker what is actually a problem). Problem occurs because of deserializing. So once I have date in xml format it tries to deserialize itself as GregorianCalendar. Other part of a question still remains: What is the best way to receive something (long timestamp or GregorianCalendar) and convert it to sql date, without making 10 different wrappers for 10 different classes. I'm using a NetBeans for code and wsdl generation.

Upvotes: 8

Views: 27411

Answers (12)

Basil Bourque
Basil Bourque

Reputation: 338306

I will expand on the correct answer by JeroenWyseur.

ISO 8601

The ISO 8601 standard format is absolutely the best way to serialize a date-time value for data exchange. The format is unambiguous, intuitive to peoples across cultures, and increasingly common around the world. Easy to read for both humans and machines.

2015-01-16T20:15:43+02:00
2015-01-16T18:15:43Z

The first example has an offset of two hours ahead of UTC. The second example shows the common use of Z ("Zulu") to indicate UTC, short for +00:00.

java.time

The java.util.Date & .Calendar classes bundled with Java are notoriously troublesome, confusing, and flawed. Avoid them. Instead use:

  • java.time package, built into Java 8, inspired by Joda-Time, defined by JSR 310.

The java.time package supplants its predecessor, the Joda-Time library.

By default, both libraries use ISO 8601 for both parsing and generating String representations of date-time values.

Note that java.time extends the ISO 8601 format by appending the proper name of the time zone, such as 2007-12-03T10:15:30+01:00[Europe/Paris].

Search StackOverflow.com for many hundreds of Questions and Answers with much discussion and example code.

Avoid Count-From-Epoch

Some of the other answers recommend using a number, a count from epoch. This approach is not practical. It is not self-evident. It is not human-readable, making debugging troublesome and frustrating.

Which number is it, whole seconds as commonly used in Unix, milliseconds used in java.util.Date & Joda-Time, microseconds commonly used in databases such as Postgres, or nanoseconds used in java.time package?

Which of the couple dozen epochs, first moment of 1970 used in Unix, year 1 used in .Net & Go, "January 0, 1900" used in millions (billions?) of Excel & Lotus spreadsheets, or January 1, 2001 used by Cocoa?

diagram of different resolution of time tracking

See my answer on a similar question for more discussion.

LocalDate

I'm passing around some objects through web service and some of them contain java.sql.Date

The replacement for the terrible java.sql.Date class is java.time.LocalDate.

Best to avoid the legacy class entirely, but you can convert back and forth by calling new methods added to the old class: myJavaSqlDate.toLocalDate()

Serializing LocalDate

The LocalDate class implements Serializable. So you should have no problem with it automatically serializing, both marshaling and unmarshalling.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Upvotes: 1

Jeroen Wyseur
Jeroen Wyseur

Reputation: 3543

Joda-Time

The Date class has a clunky API. A better implementation is Joda-Time.

ISO 8601

Joda-Time also allows you to convert your date in a ISO 8601 standard format (yyyy-mm-ddTHH:MM:SS.SSS). Using this standard when moving dates from server to its client has the advantage to include the full date in a readable format. When you use for example JAXB, the XML representation of a date is also this ISO standard. (see the XMLGregorianCalendar class)

Upvotes: 9

Matt
Matt

Reputation: 2224

one caveat with java.sql.Date that bit me recently is that it doesn't store the time portions (hours, minutes, seconds, etc) just the date portion. if you want the full timestamp you have to use java.util.Date or java.sql.Timestamp

Upvotes: 1

Argelbargel
Argelbargel

Reputation: 6210

Hmmm... Can't think of any reason why any serialized object-instance (serialized via the default java mechanism) should deserialize itself as an instance of another class as the class information should be an inherent part of the serialized data.

So it's either a problem of your (de-)serialization framework or the framework accepts any "date-like" object on the "sending end" (Calendar, java.util.Date etc. - an thus java.sql.Date too as it extends java.util.Date), "serializes" it to a String in some common date-format (so the type information is lost) and "deserializes" it back to a Calendar object on the receiving end.

So I think the simplest way to get to java.sql.Date is to do a

java.sql.Date date = new java.sql.Date(calendar.getTimeInMillis);

where you need an java.sql.Date but get the GregorianCalendar back from the "deserialization".

Upvotes: 0

Ioannis
Ioannis

Reputation: 124

java.sql.Date already implements Serializable so no need to implement it :-)

As far as your main question is concerned, I'm deeply in love with JAXB as I can turn almost any XML into an object, so it might be worth your while to look into it.

Upvotes: 0

Chris Gow
Chris Gow

Reputation: 7924

To answer the first part of your question, I would suggest a string in iso 8601 format (this is a standard for encoding dates).

For the second part, I'm not sure why you would need a proxy class? Or why you would have to extend the date class to support this. eg. would not your web service know that a certain field is a date and do the conversion from date to string and back itself? I'd need a little more information.

Upvotes: 3

Shimi Bandiel
Shimi Bandiel

Reputation: 5747

First, if you are using web services, it means you are serializing to XML and not your regular Java serialization (but some other library for marshaling and unmarshaling). So the question is lacking some information.

Second, if you have control over your InputStream & OutputStream try extending ObjectOutputStream and ObjectInputStream and override replaceObject() and resolveObject() and then you can implement serialization for java.sql.Date.

Upvotes: 0

boutta
boutta

Reputation: 24609

I've looked into the implementation of java.sql.Date and as I see it java.sql.Date is Serializable as an extension of java.util.Date.

Upvotes: 1

paul
paul

Reputation: 13516

You could use an encoder and decode to serialise and deserialise your objects.

Here is an example which serialises the SWT Rectangle class:

XMLEncoder encoder = new XMLEncoder(new FileOutputStream(file));
encoder.setPersistenceDelegate(
    Rectangle.class, 
    new DefaultPersistenceDelegate(new String[]{"x", "y", "width", "height"}));
encoder.writeObject(groups);
encoder.close();

Upvotes: 0

entzik
entzik

Reputation: 654

Serializing the long returned by Date.getTime() as previously suggested will work. You should however note that if your server is in another time zone than the client, the date you'll reconstruct on the other side will be different. If you want want to reconstruct exact same date object you also need to send your time zone (TimeZone.getID()) and use it to reconstruct the date on the other side.

Upvotes: 6

Andreas Bakurov
Andreas Bakurov

Reputation: 1541

You don't need default constructor (empty) in order to serialize/deserialize date (either java.sql.Date or java.util.Date). During deserialization constructor is not called but attributes of object set directly to values from serialized data and you can use the object as it is since it deserialized.

Upvotes: 0

cagcowboy
cagcowboy

Reputation: 30828

java.sql.Date extends java.util.Date

Just use getTime() to get the long value from it. This can be serialized and a new java.sql.Date(long) or new java.util.Date(long) constructed from it at the other end.

Upvotes: 2

Related Questions