【发布时间】:2011-07-14 23:35:03
【问题描述】:
在java中,java.util和java.sql包都包含一个Date类,那么它们有什么区别呢?
如果 Java 中存在一个 Date 类,那么需要另一个 Date 类是什么?
【问题讨论】:
在java中,java.util和java.sql包都包含一个Date类,那么它们有什么区别呢?
如果 Java 中存在一个 Date 类,那么需要另一个 Date 类是什么?
【问题讨论】:
java.util.Datejava.util.Date 类是旧的日期时间类的一部分,这些类已被证明设计不佳、令人困惑且麻烦。目的是在时间线上表示一个时刻(日期和时间)。
该类似乎代表 UTC 中的一个时刻,除了它的 toString 方法在生成字符串时静默地应用 JVM 的当前默认时区,这会产生 java.util.Date 具有时区但实际上确实存在的错觉不是。好吧,实际上它确实在其源代码深处分配了一个时区,用于内部但不明显且不可设置或获取的某些东西。一团乱麻。
java.sql.Datejava.sql.Date 表示 仅限日期的值(如 “DATE” in the SQL database world 的含义)使糟糕的情况变得更糟,但通过扩展 java.util.Date 这样做是一种黑客行为。但是你应该假装它是 not 一个子类,如类文档中所述。此外,它具有一天中的时间,但通过在UTC 中调整到一天中的第一时刻来假装没有。一团乱麻。
还有一件事……java.sql.Date 类增加了小数秒,分辨率为nanoseconds。这超出了 java.util.Date 使用的milliseconds 分辨率。许多数据库支持更精细的分辨率,例如microsecond 或纳秒。
因此,在这些旧的日期时间类中,无法真正表示没有时间和时区的仅日期值。
java.time 框架来救援。受到高度成功的Joda-Time 项目的启发。内置于 Java 8 及更高版本。 Back-ported to Java 6 & 7,还有adapted to Android。见Oracle Tutorial。
InstantInstant 类代表 UTC 时间线上的时刻,分辨率为纳秒。简洁明了。
Instant instant = Instant.now();
instant.toString() → 2016-06-19T02:34:55.564Z
如果需要 java.util.Date 与旧代码一起使用,您可以转换。查看添加到旧类的新方法:java.util.Date::toInstant() 和 java.util.Date.from( Instant )。
要适应某个地区的wall-clock time,请应用offset-from-UTC (ZoneOffset) 以获取OffsetDateTime 对象。更好的是,如果您知道正确的时区名称,请应用 ZoneId 以获取 ZonedDateTime。
例如,America/Montreal 在夏季 Daylight Saving Time (DST) 下比 UTC 晚四个小时,因此我们在下面的代码中看到上一个日期的晚上 10 点(22:00)而不是凌晨 2 点在上面的例子中。回溯四个小时,跨越午夜,进入前一个日期。但是上面看到的instant 对象和接下来看到的zdt 对象都代表时间轴上的同一时刻。历史上的同一时刻,但在巴黎、加尔各答和奥克兰是“明天”,而在蒙特利尔、墨西哥城和檀香山是“昨天”。
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
zdt.toString() → 2016-06-18T22:34:55.564-04:00[美国/蒙特利尔]
LocalDate对于仅日期值,请使用 LocalDate 类。不保留时间和时区。
LocalDate localDate = zdt.toLocalDate();
但请注意,时区对于确定日期至关重要,因为对于任何特定时刻,日期都可能因全球时区而异。因此,上面关于指定时区的讨论。例如,接下来看看我们如何获得六月的 18 日 而不是 19 日。
localDate.toString() → 2016-06-18
如果时区日期之间的这种区别对您的业务很重要,您应该使用存储在TIMESTAMP WITH TIME ZONE而不是DATE的数据库列类型中的日期时间值。
搜索 Stack Overflow 以获取更多信息和 java.time 的许多示例(标签:java-time)。
所以保存到数据库,使用JDBC driver 符合JDBC 4.2 spec(见Guide)。在PreparedStatement上调用setObject和getObject直接使用java.time对象。
如果您的 JDBC 驱动程序还不支持直接使用 java.time 类型,请使用添加到旧类中的新方法来促进转换。但尽量减少对旧类的使用;立即转换为 java.time 并在您的业务逻辑中仅使用 java.time 对象。
java.sql.Date mySqlDate = java.sql.Date.valueOf( localDate );
还有另一个方向。
LocalDate localDate = mySqlDate.toLocalDate();
【讨论】:
这些答案似乎已经部分过时了。
我刚刚阅读了一点 API 代码(Java 版本 1.8.0_91)并在 java.sql.Date 中找到了这个:
/**
* Creates a date which corresponds to the day determined by the supplied
* milliseconds time value {@code theDate}.
*
* @param theDate
* a time value in milliseconds since the epoch - January 1 1970
* 00:00:00 GMT. The time value (hours, minutes, seconds,
* milliseconds) stored in the {@code Date} object is adjusted to
* correspond to 00:00:00 GMT on the day determined by the supplied
* time value.
*/
public Date(long theDate) {
super(normalizeTime(theDate));
}
/*
* Private method which normalizes a Time value, removing all low
* significance digits corresponding to milliseconds, seconds, minutes and
* hours, so that the returned Time value corresponds to 00:00:00 GMT on a
* particular day.
*/
private static long normalizeTime(long theTime) {
return theTime;
}
标准化时间的方法仍然存在,甚至评论说时间将标准化为格林威治标准时间 00:00:00,但它什么也没做。由于某种原因,他们删除了规范化,这意味着 java.sql.Date 就像 java.util.Date 一样包含自 1.1.1970 以来的毫秒数。所以有时间分量,但对外不显示。
例如代码
java.util.Date utilDate = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(Calendar.getInstance().getTimeInMillis())
System.out.println(utilDate);
System.out.println(sqlDate);
产生输出
Thu Jun 02 13:17:35 CEST 2016
2016-06-02
所以要小心 sql 日期,不要像只包含日期而没有时间信息那样处理它们。例如:
java.sql.Date sqlDate1 = new java.sql.Date(Calendar.getInstance().getTimeInMillis());
java.sql.Date sqlDate2 = new java.sql.Date(Calendar.getInstance().getTimeInMillis());
System.out.println(sqlDate1);
System.out.println(sqlDate2);
System.out.println(sqlDate1.equals(sqlDate2));
System.out.println(sqlDate1.toString().equals(sqlDate2.toString()));
打印:
2016-06-02
2016-06-02
false
true
【讨论】:
java.sql.Date 接受一个长整数,表示自 1970 年 1 月 1 日以来的毫秒数。如果给定的数字为负数,则表示 1970 年 1 月 1 日之前的时间。请记住,对象内的封装值仅表示日期比如 1970 年 1 月 1 日,没有存储时间信息。
java.util.Date 存储日期和时间信息。 它比 java.sql.Date 更常用。
【讨论】:
来自the JavaDoc of java.sql.Date:
一个毫秒值的瘦包装器,允许 JDBC 将其识别为 SQL DATE 值。毫秒值表示自 1970 年 1 月 1 日 00:00:00.000 GMT 以来经过的毫秒数。
为了符合 SQL DATE 的定义,
java.sql.Date实例包装的毫秒值必须通过在实例所在的特定时区中将小时、分钟、秒和毫秒设置为零来“规范化”关联。
解释:一个java.util.Date代表日期和时间,一个java.sql.Date只代表一个日期(java.sql.Date的补码是java.sql.Time,它只代表一个时间的一天,但也扩展java.util.Date)。
【讨论】:
Java.util.Date 是通用的通用日期对象。它存储一个日期(作为一个长的)并允许您显示它。
java.sql.Date 扩展了 java.util.Date。需要注意的主要区别是 java.sql.Date 没有时间组件。
【讨论】: