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

Ilj*_* S. 7 java date java.util.date

等等,不要急于回答"java.util.Date",请考虑以下场景.

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

你从db中取出它,"生日"将由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组件中一样......

Bas*_*que 6

其他答案使用过时的类.

java.time

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

LocalDate

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

LocalDate dateOfBirth = LocalDate.new( 1979 , 1 , 10 );
Run Code Online (Sandbox Code Playgroud)

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

ZonedDateTime

对于一次会议,我们不能那么宽松 - 愚蠢.我们需要一个日期,时间和时区.在java.time中表示ZonedDateTime类.该时区是关键要素从问题的情况下丢失.添加时区,一切都很好.

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

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

调整时区

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

ZoneId zoneIdParis = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdtParis = zdtMontreal.withZone( zoneIdParis );
Run Code Online (Sandbox Code Playgroud)

我们在两个时尚中以两个对象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数据类型).您的JDBC驱动程序会为您调解这两种类型.

最终将更新JDBC驱动程序以直接使用java.time类型.但在那之前我们必须转换为java java.sql.Date和.s等类型的.s java.sql.Timestamp.旧类中添加了新方法以支持这些转换.

java.sql.Date sqlDateOfBirth = java.sql.Date.valueOf( dateOfBirth );
java.sql.Timestamp sqlMeeting = java.sql.Timestamp.valueOf( zdtMontreal );
Run Code Online (Sandbox Code Playgroud)

然后调用setDatesetTimestamp你的PreparedStatement.

走向另一个方向,从数据库到Java,电话getDategetTimestamp你的ResultSet.然后立即转换为java.time类型,避免在业务逻辑中使用java.sql类型.

对于日期时间值,我们必须通过一个Instant对象.这InstantUTC时间轴上的一个时刻.我们应用时区来为用户获取挂钟时间.

LocalDate dateOfBirth = mySqlDate.toLocalDate();
Instant instant = mySqlTimestamp.toInstant();
ZonedDateTime zdtMontreal = ZonedDateTime.ofInstant( instant , zoneIdMontreal );
Run Code Online (Sandbox Code Playgroud)


Jer*_*erg 5

使用LOCALDATE的JodaTime并且只存储日期的生日,而不是时间.