其他答案使用过时的类。
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.of( 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)可以解决问题。
数据库
您的数据库应该对生日使用仅日期类型,并为会议使用带时区的时间戳类型(请参阅Wikipedia 和数据库文档中的SQL 数据类型)。您的 JDBC 驱动程序为您调解这两种类型。
最终 JDBC 驱动程序将被更新为直接使用 java.time 类型。但在此之前,我们必须转换为 java.sql 类型,例如java.sql.Date和java.sql.Timestamp。新方法已添加到旧类中以支持这些转换。
java.sql.Date sqlDateOfBirth = java.sql.Date.valueOf( dateOfBirth );
java.sql.Timestamp sqlMeeting = java.sql.Timestamp.valueOf( zdtMontreal );
然后在您setDate的.setTimestampPreparedStatement
换个方向,从数据库到 Java,调用getDate你getTimestamp的ResultSet. 然后立即转换为 java.time 类型,避免在业务逻辑中使用 java.sql 类型。
对于日期时间值,我们必须通过一个Instant对象。这是UTCInstant时间线上的一个时刻。我们应用时区来为用户获取挂钟时间。
LocalDate dateOfBirth = mySqlDate.toLocalDate();
Instant instant = mySqlTimestamp.toInstant();
ZonedDateTime zdtMontreal = ZonedDateTime.ofInstant( instant , zoneIdMontreal );