【问题标题】:UnsupportedOperationException - Why can't you call toInstant() on a java.sql.Date?UnsupportedOperationException - 为什么不能在 java.sql.Date 上调用 toInstant()?
【发布时间】:2016-04-05 19:35:04
【问题描述】:

java.util.Date 类有一个名为toInstant() 的方法,该方法将Date 实例转换为java.time.Instant

java.sql.Date 类扩展了 java.util.Date 类,但是当我尝试在 java.sql.Date 上调用 toInstant() 时,我收到了一个 UnsupportedOperationException

为什么toInstant()java.sql.Date 的操作不受支持?

java.sql.Date 转换为java.time.Instant 的“正确”方法是什么?

【问题讨论】:

    标签: java java-8 java-time


    【解决方案1】:

    java.sql.Datejava.time之间的正确映射是LocalDate

    LocalDate date = sqlDate.toLocalDate();
    

    如果你真的必须,你可以推导出一个Instant,尽管额外的信息(时间)是任意的。例如:

    Instant i = date.atStartOfDay(ZoneOffset.UTC).toInstant();
    

    【讨论】:

    • 希望我能接受两个答案......这完美地回答了我问题的第二部分,但主要问题是为什么你不能在 java.sql.Date 上调用 toInstant(),所以我有接受@YassinHajajanswer。这个答案需要更多的支持。
    • 由于date 是LocalDate(本地时区),转换为Instant(即UTC)不应该指定本地时区吗?:Instant i = date.atStartOfDay(ZoneId.systemDefault()).toInstant()
    【解决方案2】:

    根据JavaDoc

    由于sql.Date 没有时间组件,因此无法将其转换为time.Instant

    此方法总是抛出 UnsupportedOperationException 并且应该 不能使用,因为 SQL 日期值没有时间组件。

    【讨论】:

    • 现在,问题是为什么它是这样设计的。可以想象它的默认时间信息设置为 0。在邮件列表中找不到任何内容。
    • @Tunaki 这个逻辑让我觉得是因为父类util.Date 有一个公共的toInstant 方法不应该被子类使用。因此,他们没有将其设为final(以保留继承逻辑),而是更喜欢覆盖它并使其无法使用的选项。
    • @Tunaki 可能是因为,正如您所指出的,任何转换为​​ Instant 都需要任意设置一些数据(时间、时区)。
    【解决方案3】:

    java.sql.Date 仅支持日期组件(日期、月份、年份)。它不支持时间组件(小时、分钟、秒、毫秒)。 toInstant 需要 Date 和 Time 组件,因此 java.sql.Date 实例上的 toInstant 会抛出 UnsupportedOperationException 异常。

    到即时 Java 文档

    这个方法总是抛出一个 UnsupportedOperationException 并且不应使用,因为 SQL 日期 值没有时间分量。

    java.util.Date OR java.sql.Timestamp 有两个日期/时间组件,所以 toInstant() 有效!

    你可以这样做:

    // Time is 00:00:00.000
    
    new java.util.Date(sqlDate.getTime()).toInstant() 
    

    更新:

    Instant.ofEpochMilli(sqlDate.getTime());
    
    // and
    new java.util.Date(sqlDate.getTime()).toInstant();
    

    将返回相同的结果,因为 toInstant() 方法在内部调用 Instant.ofEpochMilli(getTime())。

    public Instant toInstant() {
        return Instant.ofEpochMilli(getTime());
    }
    

    【讨论】:

    • 哪个更“正确”? Instant.ofEpochMilli(sqlDate.getTime());new java.util.Date(sqlDate.getTime()).toInstant();
    • 您不能在不提供时区的情况下将 java.sql.Date 转换为 Instant。在 java.sql.Date 调用 getTime() 是没有意义的。正如其他 cmets 所指出的,java.sql.Date 只是一个 LocalDate,并不代表时间的瞬间。
    • 我没有在 java sql 日期调用 toInstant。我确实在 java util date 上调用了 toInstant,这对这种情况来说没问题。
    【解决方案4】:

    到目前为止给出的答案集中在java.sql.Date没有时间信息的细节上。这是正确的,但不是此类型无法直接转换为Instant 的真正或充分理由。不幸的是,documentation of Java-8 确实犯了同样的错误,让用户认为问题只是因为缺少时间信息。

    从概念上讲,java.sql.Date 类型表示本地类型。它模拟了一个日历日期,该日期在我们全球的任何地区都可能不同。但是Instant 在我们的地球上是一样的。 因此用户需要时区或时区偏移量来进行转换。

    可悲的是,java.sql.Date 类型继承自 java.util.Date,这是一个全局类型(类即时)。但是,这种继承实际上表示实现继承,而不是类型继承。考虑破坏这些旧 JDBC 类的设计的另一个原因。因此,确实可以使用 hack 通过其方法 getTime()java.sql.Date 包装在 java.util.Date 的实例中,这最终允许直接转换为瞬间。但是:此转换隐式使用系统的默认时区。

    那么如何以迂腐的方式正确转换呢?让我们再次考虑documentation of Java-8,这里指向正确的方向:

    java.sql.Date sqlDate = ...;
    LocalDate calendarDate = sqlDate.toLocalDate();
    ZonedDateTime zdt = calendarDate.atStartOfDay(ZoneId.of("Europe/Paris"));
    Instant instant = zdt.toInstant();
    

    【讨论】:

      【解决方案5】:

      如果您的日期没有时间 - 将其转换为毫秒:

      Instant.ofEpochMilli(date.getTime())
         .atZone(ZoneId.systemDefault())
         .toLocalDate();
      

      【讨论】:

        猜你喜欢
        • 2018-03-29
        • 2020-10-29
        • 2015-09-29
        • 2015-08-20
        • 1970-01-01
        • 2013-01-17
        • 1970-01-01
        • 2019-03-02
        • 2017-10-14
        相关资源
        最近更新 更多