开发者

How would you represent date of birth in your java model?

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.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.

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 );


Use LocalDate from JodaTime and only store the date for the birthday, not the time.


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.


if you are dealing with dates, you are better of using joda time. You can build a DateTime object with date/time and timezone info so you have all info needed to deal with different timezones.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜