And wait, don't rush to answer "java.util.Date", consider the following scenario.
Person object having 2 fields: "birthday" and "nextMeeting" both java.util.Date. Now birthday stored in database as date type column (no time) for eg. 01-10-1979, and nextMeeting as datetime type for ex. 01-10-2010 20:00:00.
You pull it from db, "birthday" will be auto set to midnight by JDBC. Now you need to send this object to other JVM using lets say RMI or whatever technology.
On the other end JVM has timezone -1h from originating JVM. This is where problem starts. nextMeeting become 01-10-2010 19:00:00 which is absolutely FINE and CORRECT from user perspective etc...
BUT birthday become 30-09-1979 23:00:00 which will be represented to user as 30th of September, which is really not what we want, cause obviously birthday is something static and NOT dependent on timezones.
So column type in db chosen correctly (date). This type of column usually represented as java.util.Date. But in our case it is wrong java type to use.
So how would you represent a birthday? Consider that you need to manipulate this object on a UI like in a datepicker component etc...
The other answers use outmoded classes.
Both Joda-Time and the old java.util.Date/.Calendar classes have been supplanted by the java.time framework built into Java 8 and later. Defined by JSR 310. Extended by the ThreeTen-Extra project. Back-ported to Java 6 & 7 by the ThreeTen-BackPort project, which is wrapped for Android by the ThreeTenABP project.
LocalDate
A date-only value without time-of-day and without time zone can be represented by the LocalDate
class. Such a class was lacking in the old date-time classes bundled with the earliest versions of Java. The old java.sql.Date
class pretends to be date-only but in fact has a time-of-day inherited from java.util.Date
(a date-time value).
LocalDate dateOfBirth = LocalDate.of( 1979 , 1 , 10 );
Without a time of day nor time zone, a date of birth is inherently inaccurate for determining one’s age. But in nearly all use-cases we don't care; give-or-take part of a day is close enough.
ZonedDateTime
For a meeting we cannot be so loosey-goosey. We need a date, a time, and a time zone. In java.time that means ZonedDateTime
class. The time zone is the key element missing from the scenario in the Question. Add the time zone and all is well.
ZoneId zoneIdMontreal = ZoneId.of( "America/Montreal" );
ZonedDateTime zdtMontreal = ZonedDateTime.of( 2010 , 1 , 10 , 20 , 0 , 0 , zoneIdMontreal );
Now communicate the objects to another machine. Both remain intact, the same date-only value for date-of-birth (1979-01-10) and the same Montréal date-time for the meeting.
You might then want to adjust that meeting to another time zone expected by the person using this other machine.
ZoneId zoneIdParis = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdtParis = zdtMontreal.withZone( zoneIdParis );
We have the same moment on the timeline represented in two fashions, in two objects, zdtMontreal & zdtParis.
LocalDateTime
If your nextMeeting
has no time zone nor offset-from-UTC info, then represent as a LocalDateTime
object. In the database it would be stored as a type like TIMESTAMP WITHOUT TIME ZONE
.
Such values do not represent a moment in the timeline. They represent only a range of possible moments. To determine an actual moment you must provide the context of a specific time zone.
For storing future date-time values such as a planned meeting further out than a few weeks, doing so without time zone may be appropriate. Politicians around the world have shown a proclivity for often changing Daylight Saving Time and otherwise redefining their time zones. And they often do so with little warning, as little as only several weeks warning.
To determine an actual moment such as showing a schedule in a calendar, apply a time zone ZoneId
to get a ZonedDateTime
.
The java.time classes use the standard ISO 8601 formats by default when parsing/generating textual representations of date-time values. The ZonedDateTime
class goes one step further by extending ISO 8601 to append the name of the time zone in square brackets.
If serializing values via text, use to the sensible and unambiguous formats of ISO 8601.
None of the issues raised in the Question remain. Using an excellent date-time library and ISO 8601 such as java.time solves the problem.
Your database should use date-only type for the birthdate, and a timestamp-with-time-zone type for the meeting (see SQL data types in Wikipedia and in your database’s documentation). Your JDBC driver mediates both types for you.
Eventually JDBC drivers will be updated to directly use java.time types. But until then we must convert to java.sql types such as java.sql.Date
and java.sql.Timestamp
. New methods have been added to the old classes to support these conversions.
java.sql.Date sqlDateOfBirth = java.sql.Date.valueOf( dateOfBirth );
java.sql.Timestamp sqlMeeting = java.sql.Timestamp.valueOf( zdtMontreal );
Then call setDate
and setTimestamp
on your PreparedStatement
.
Going the other direction, from database to Java, call getDate
and getTimestamp
on your ResultSet
. Then translate immediately to java.time types, avoiding the use of the java.sql types in your business logic.
For the date-time value, we must go through an Instant
object. The Instant
is a moment on the timeline in UTC. We apply a time zone to get a wall-clock time for the user.
LocalDate dateOfBirth = mySqlDate.toLocalDate();
Instant instant = mySqlTimestamp.toInstant();
ZonedDateTime zdtMontreal = ZonedDateTime.ofInstant( instant , zoneIdMontreal );
Use LocalDate from JodaTime and only store the date for the birthday, not the time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With