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.
java.time
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.new( 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.
Adjust time zone
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
.
ISO 8601
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.
Database
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 );
Somehow, the two java systems will have to agree on Calendar/TimeZone information, or the Date object will need to be converted to a timestamp when being passed to the remote system.
The simplest method might be to simply require all clients to treat the birthday as a GMT time --- when they display/compare/whatever the birthdays, have them create a Calendar
with the "GMT"
TimeZone
, and then setTime()
on it with the supplied Date
.
If you're working with the model locally at all, you should really have a Date object, not just a timestamp.
For manipulation i will advise java.util.Calendar
For representation
Birthday as java.util.Date
NextMeetin as java.sql.Timestamp
That's a very good question...
Android for example stores birthday as String in 'yyyy-MM-dd' format. I was wondering why they don't use java.util.Date and I guess that the reason would be the same problem which you brought here.
So I would recomment either String or some "Timezone-independent-date". But after few minutes searching in Java and joda-time documentation I have no idea how to do it.
EDIT: Seems @Jeroen is right - use LocalDate.
来源:https://stackoverflow.com/questions/4031008/how-would-you-represent-date-of-birth-in-your-java-model