【问题标题】:Difference between Date class in Package java.util & Package java.sql包 java.util 和包 java.sql 中的 Date 类之间的区别
【发布时间】:2011-07-14 23:35:03
【问题描述】:

在java中,java.utiljava.sql包都包含一个Date类,那么它们有什么区别呢?

如果 Java 中存在一个 Date 类,那么需要另一个 Date 类是什么?

【问题讨论】:

    标签: java date


    【解决方案1】:

    java.util.Date

    java.util.Date 类是旧的日期时间类的一部分,这些类已被证明设计不佳、令人困惑且麻烦。目的是在时间线上表示一个时刻(日期和时间)。

    该类似乎代表 UTC 中的一个时刻,除了它的 toString 方法在生成字符串时静默地应用 JVM 的当前默认时区,这会产生 java.util.Date 具有时区但实际上确实存在的错觉不是。好吧,实际上它确实在其源代码深处分配了一个时区,用于内部但不明显且不可设置或获取的某些东西。一团乱麻。

    java.sql.Date

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

    Instant

    Instant 类代表 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上调用setObjectgetObject直接使用java.time对象。

    如果您的 JDBC 驱动程序还不支持直接使用 java.time 类型,请使用添加到旧类中的新方法来促进转换。但尽量减少对旧类的使用;立即转换为 java.time 并在您的业务逻辑中仅使用 java.time 对象。

    java.sql.Date mySqlDate = java.sql.Date.valueOf( localDate );
    

    还有另一个方向。

    LocalDate localDate = mySqlDate.toLocalDate();
    

    【讨论】:

      【解决方案2】:

      这些答案似乎已经部分过时了。

      我刚刚阅读了一点 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
      

      【讨论】:

        【解决方案3】:

        java.sql.Date 接受一个长整数,表示自 1970 年 1 月 1 日以来的毫秒数。如果给定的数字为负数,则表示 1970 年 1 月 1 日之前的时间。请记住,对象内的封装值仅表示日期比如 1970 年 1 月 1 日,没有存储时间信息。

        java.util.Date 存储日期和时间信息。 它比 java.sql.Date 更常用。

        【讨论】:

          【解决方案4】:

          来自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)。

          【讨论】:

            【解决方案5】:

            Java.util.Date 是通用的通用日期对象。它存储一个日期(作为一个长的)并允许您显示它。

            java.sql.Date 扩展了 java.util.Date。需要注意的主要区别是 java.sql.Date 没有时间组件。

            【讨论】:

              猜你喜欢
              • 2010-10-22
              • 2013-01-20
              • 2014-06-15
              • 1970-01-01
              • 2018-11-09
              • 2017-02-14
              • 1970-01-01
              • 2013-08-15
              相关资源
              最近更新 更多