您如何在您的Java模型中表示出生日期?

时间:2010-10-27 07:53:29

标签: java date java.util.date

等等,不要急于回答“java.util.Date”,请考虑以下情况。

具有2个字段的人物对象:“birthday”和“nextMeeting”都是java.util.Date。 现在生日存储在数据库中作为日期类型列(没有时间),例如。 01-10-1979,和next作为日期时间类型的例子。 01-10-2010 20:00:00。

你从db中取出它,“birthday”会被JDBC自动设置为午夜。 现在,您需要使用RMI或任何技术将此对象发送到其他JVM。

另一方面,JVM与原始JVM的时区为-1h。这是问题开始的地方。 nextMeeting成为01-10-2010 19:00:00,从用户的角度来看,这绝对是精细和正确的......

但是生日变成30-09-1979 23:00:00将在9月30日向用户显示,这实际上不是我们想要的,因为显然生日是静态的,不依赖于时区。

因此,db中的列类型选择正确(日期)。这种类型的列通常表示为java.util.Date。但在我们的例子中,使用它是错误的java类型。

那你怎么代表一个生日?考虑您需要在UI上操作此对象,例如在datepicker组件等...

6 个答案:

答案 0 :(得分:6)

其他答案使用过时的类。

java.time

Joda-Time和旧的java.util.Date/.Calendar类已被Java 8及更高版本中内置的java.time框架所取代。由JSR 310定义。由ThreeTen-Extra项目扩展。后端移植到Java 6& 7由ThreeTen-BackPort项目提供,由ThreeTenABP项目包装为Android。

LocalDate

LocalDate类可以表示没有时间且没有时区的仅限日期的值。与最早版本的Java捆绑在一起的旧日期时间类中缺少这样的类。旧的java.sql.Date类假装仅限日期,但实际上是从java.util.Date(日期时间值)继承的时间。

LocalDate dateOfBirth = LocalDate.new( 1979 , 1 , 10 );

如果没有时间或时区,出生日期本身就不准确确定一个人的年龄。但在几乎所有用例中我们都不关心;让一天的部分时间足够接近。

ZonedDateTime

对于一次会议,我们不能那么宽松 - 愚蠢。我们需要一个日期,时间和时区。在java.time中,表示ZonedDateTime类。 时区是问题中方案中缺少的关键元素。添加时区,一切都很好。

ZoneId zoneIdMontreal = ZoneId.of( "America/Montreal" );
ZonedDateTime zdtMontreal = ZonedDateTime.of( 2010 , 1 , 10 , 20 , 0 , 0 , zoneIdMontreal );

现在将对象传达给另一台机器。两者都保持完整,出生日期(1979-01-10)的相同日期值和会议的蒙特利尔日期时间相同。

调整时区

然后,您可能希望将该会议调整为使用此其他计算机的人员所期望的另一个时区。

ZoneId zoneIdParis = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdtParis = zdtMontreal.withZone( zoneIdParis );

我们在以两种方式表示的时间轴上有相同的时刻,两个对象,zdtMontreal& zdtParis。

LocalDateTime

如果您的nextMeeting没有时区,也没有偏离UTC信息,则表示为LocalDateTime个对象。在数据库中,它将存储为类似TIMESTAMP WITHOUT TIME ZONE的类型。

这些值代表时间轴中的一个时刻。它们只代表一系列可能的时刻。要确定实际时刻,您必须提供特定时区的上下文。

为了存储未来的日期时间值,例如计划会议的时间超过几周,这样做可能是合适的。世界各地的政治家们都表现出了经常改变夏令时和重新定义时区的倾向。他们经常在没有警告的情况下这样做,只需要几周的警告。

要确定实际时刻,例如在日历中显示日程安排,请应用时区ZoneId以获取ZonedDateTime

ISO 8601

在解析/生成日期时间值的文本表示时,java.time类默认使用标准ISO 8601格式。 ZonedDateTime类更进一步,扩展了ISO 8601,将时区名称附加在方括号中。

如果通过文本序列化值,请使用ISO 8601的合理且明确的格式。

问题中提出的问题都没有。使用优秀的日期时间库和ISO 8601(例如java.time)解决了这个问题。

数据库

您的数据库应该使用仅限日期类型的生日日期,以及会议的时间戳与时区类型(请参阅维基百科和数据库文档中的SQL data types)。您的JDBC驱动程序会为您调解这两种类型。

最终将更新JDBC驱动程序以直接使用java.time类型。但在此之前,我们必须转换为java {s}类型,例如java.sql.Datejava.sql.Timestamp。旧类中添加了新方法以支持这些转换。

java.sql.Date sqlDateOfBirth = java.sql.Date.valueOf( dateOfBirth );
java.sql.Timestamp sqlMeeting = java.sql.Timestamp.valueOf( zdtMontreal );

然后在setDate上致电setTimestampPreparedStatement

从另一个方向,从数据库到Java,在getDate上拨打getTimestampResultSet。然后立即转换为java.time类型,避免在业务逻辑中使用java.sql类型。

对于日期时间值,我们必须通过Instant对象。 InstantUTC中时间轴上的一个时刻。我们应用时区为用户获取wall-clock time

LocalDate dateOfBirth = mySqlDate.toLocalDate();
Instant instant = mySqlTimestamp.toInstant();
ZonedDateTime zdtMontreal = ZonedDateTime.ofInstant( instant , zoneIdMontreal );

答案 1 :(得分:5)

使用LocalDate中的JodaTime,仅存储生日的日期,而不是时间。

答案 2 :(得分:1)

不知何故,两个java系统必须同意Calendar / TimeZone信息,或者Date对象在传递到远程系统时需要转换为时间戳。

最简单的方法可能是简单地要求所有客户将生日视为GMT时间 - 当他们显示/比较/无论生日时,让他们用Calendar创建"GMT" TimeZone,然后setTime()使用提供的Date

如果你在本地使用模型,你应该有一个Date对象,而不仅仅是一个时间戳。

答案 3 :(得分:0)

对于操作,我会建议java.util.Calendar

代表

  

生日为java.util.Date
  NextMeetin as java.sql.Timestamp

答案 4 :(得分:0)

这是一个非常好的问题......

Android例如以'yyyy-MM-dd'格式将生日存储为String。我想知道他们为什么不使用java.util.Date,我想原因就是你带来的问题。

所以我会推荐String或一些“Timezone-independent-date”。但是在用Java和joda-time文档搜索几分钟后,我不知道该怎么做。

编辑:似乎@Jeroen是对的 - 使用LocalDate。

答案 5 :(得分:-1)

如果您正在处理日期,则最好使用joda time。您可以使用日期/时间和时区信息构建DateTime对象,以便获得处理不同时区所需的所有信息。