How can I compare dates in a reliable way in Java before and after they are persisted?
The problem I face is: When I create a new instance of java.util.Date it's toString() method returns a value including the day of the week and time zone:
Fri Feb 03 10:15:31 CET 2017
When I persist this date (EDIT: the Date object) in a database table and load it back into an entity the toString method returns a different format, depending on the database type:
In case of MySQL date: 2017-02-03
In case of MySQL datetime: 2017-02-03 10:24:34.0
I don't always have access to a formatter because to toString method may be called implicitly by another toString method in my application.
These questions arise for me:
How does the Date object know which format to pick when using
toString?
How can I control for the new Date object that it shall
represent a date only (that is, a day without the time fraction) in the first place?
What is the best practice to compare dates of persisted objects in
unit tests between an object before and after loading it from a
database ?
解决方案
Use objects, not strings
Do not use strings to pass data to/from your database. Use objects. That is the purpose of JDBC, to convert from the data types of your database to the data types (classes) of Java.
Use java.time, not legacy classes
Do not use the legacy date-time classes such as java.util.Date as they are notoriously troublesome, confusing, and flawed. Now legacy, supplanted by the java.time classes.
How does the Date object know which format to pick when using toString?
The format used by toString is hard-coded, not picked. You always get the same format from that method. And a poor choice of format, whereas modern libraries and protocols use standard ISO 8601 formats.
Among the many poor design choices in java.time.Date is behavior of the toString method applying the JVM’s current default time zone to a value that is actually in UTC. This creates the illusion of a time zone that is not actually present.
Better to avoid these strings. Pass and fetch objects rather than strings, between Java and database. Call PreparedStatement::setObject and ResultSet::getObject methods to work with LocalDate, Instant, and other such java.time objects.
LocalDate ld = myResultSet.getObject( … );
If your JDBC driver is not yet compliant with JDBC 4.2, and cannot directly handle java.time objects, fall back to briefly using the java.sql types. Immediately convert those java.sql objects to java.time objects by calling new methods added to the old classes.
java.sql.Date myJavaSqlDate = myResultSet.getDate( … );
java.time.LocalDate ld = myJavaSqlDate.toLocalDate();
How can I control for the new Date object that it shall represent a date only (that is, a day without the time fraction) in the first place?
Use the LocalDate class for a date-only value without time-of-day and without time zone. This maps to equivalent of the SQL standard DATE type. You should be using a date-only type to define the column in your database.
What is the best practice to compare dates of persisted objects in unit tests between an object before and after loading it from a database ?
The LocalDate class offers methods for comparison, such as compareTo, isAfter, isBefore, and isEqual. Other classes are similar.
This has all been covered many times on Stack Exchange. Please search for class names such as LocalDate, Instant, OffsetDateTime, ZonedDateTime, ZoneId, and java.sql.Timestamp.