当前位置: 首页 > 知识库问答 >
问题:

为什么JDBC在保存日期时要将时间调整为默认时区?

许博易
2023-03-14

问题是Java Date对象不存储时区。该值始终使用UTC,并在给定的时区(通常是JVM的默认时区)中进行解析和格式化。

Oracle日期列也不带时区存储,但应该表示用户看到的日期。在99.99%的情况下,这意味着JVM默认时区中的日期。

因此,JDBC驱动程序采用UTC格式的时间戳/日期值,将其转换为默认时区,并将其保存到数据库中。

    null

所以最终的问题是,为什么它是这样设计的?原因何在?

共有1个答案

柳逸春
2023-03-14

日期-时间处理是一个令人惊讶的复杂主题。我们对时间的直觉理解对我们作为程序员不利,使这个主题难以掌握。此外,旧数据库和旧类中糟糕的日期时间处理使这项工作更加混乱。

首先,避免与最早版本的Java捆绑在一起的糟糕的旧日期时间类。不要使用java.util.datejava.util.Calendarjava.sql.timestamp或其他相关类。只使用java.time类。如果必须与尚未更新为java.time的旧代码进行接口,请调用添加到旧类中的新转换方法。

日期即时替换。

不是真的。即时(和日期)始终使用UTC。modern和legacy类都表示自1970年第一个时刻以来的小数秒数,用UTC表示,1970-01-01T00:00:00Z。总是在UTC,容易的。

Oracle日期列也不带时区存储,

真的。

但应该表示用户看到的日期。在99.99%的情况下,这意味着JVM默认时区中的日期。

我不知道作者这么说是什么意思。我认为这是他们笨拙的说法,即任何类似于SQL标准时间戳而没有时区的类型都只是按原样接受任何给定的日期或日期,而不试图在区域或偏移量之间进行调整。因此,如果您在2018年1月21日中午通过,它将存储一个相当于这个字符串2018-01-23t12:00的值,而不考虑那是魁北克蒙特雷的中午还是印度加尔各答的中午(两个不同的时刻,相隔几个小时)。

因此,JDBC驱动程序采用UTC格式的时间戳/日期值,将其转换为默认时区,并将其保存到数据库中。

    null

不按原样调整和保存值(UTC)到底有什么错?

JDBC驱动程序不应该对Oracle日期或SQL标准时间戳类型的UTC进行任何调整,而没有时区

如果在2018-06-06t21:53z的两个用户,一个在魁北克,一个在印度,同时将他们自己狭隘的挂钟时间的当前时刻保存到SQL类型的列中,标准时间戳不带时区或Oracle日期,那么我们应该看到两行具有值:

  • 2018-06-06T17:53(通知日期为“昨天”)
  • 2018-06-07T03:23(通知日期为“明天”)

值不同,因为美洲/蒙特利尔比世界协调时晚4小时,而亚洲/加尔各答比世界协调时早5个半小时,并且没有对时区进行调整。再次重复一下,这里存储的值只表示日期和一天中的时间,但没有任何时区上下文或从UTC偏移量,它们不表示时刻。

造成混乱的原因可能是,某些数据库(如Postgres)确实将传入的值调整为UTC for values标题为不同类型的列,即Timestamp WITH TIME Zone类型(请注意WITHWITHWITH)。Postgres和其他数据库使用任何传递的区域/偏移量信息调整为UTC值,然后丢弃区域/偏移量信息。因此,类型名有点用词不当,您可以将其视为与时区有关的timestamp

如果上面在2018-06-06T21:53z看到的这两个用户将当前时刻保存到带有时区timestamp类型的SQL标准列中,那么这两行将显示为:

  • 2018-06-06T21:53Z
  • 2018-06-06T21:53Z

末尾的z发音为zulu,表示UTC。

instant类以UTC表示时间线上的一个时刻,分辨率为纳秒(最多九(9)位小数分数)。

Instant instant = Instant.now() ;  // Capture the current moment in UTC. 

商店。

String sql = "INSERT INTO tbl ( event ) VALUES ( ? ) ;" ;       // Writing a moment into a column of type `TIMESTAMP WTH TIME ZONE`.  
myPreparedStatement.setObject( 1 , instant ) ;                  // As of JDBC 4.2 and later, we can directly exchange java.time objects with our database.

取回。

Instant instant = myResultSet.getObject( … , Instant.class ) ;
ZoneId z = ZoneId.of( "Asia/Kolkat" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment, same point on the timeline, different wall-clock time.

要了解更多信息,请参阅Oracle教程。并搜索堆栈溢出的许多例子和解释。规范是JSR310。

您可以直接与数据库交换java.time对象。使用符合JDBC4.2或更高版本的JDBC驱动程序。不需要字符串,也不需要java.sql.*类。

从哪里获得java.time类?

    null
    null
    null

 类似资料:
  • 问题内容: 我目前正在使用当前习惯用法创建UTC DateTime对象 是否有任何默认方法,所以我可以使用默认构造函数创建基于UTC的DateTime对象,从而使其更加隐式? 问题答案: 如果您只想为joda时间设置默认时区,请使用。 如果要更改整个jvm使用的时区,请使用方法。只需确保将其设置为早,因为它可以按joda时间进行缓存。.. 引用: 默认时区来自系统属性user.timezone。如

  • 问题内容: 是否可以使用NOW()将默认时间添加为10分钟? 我已经尝试过类似的方法: 但是,它不起作用。 问题答案: 我不认为你可以做到这一点。 在MySQL的文件指出: 数据类型规范中的DEFAULT value子句指示列的默认值。除一个例外,默认值必须为常数;否则为0。它不能是函数或表达式。例如,这意味着您不能将日期列的默认值设置为诸如NOW()或CURRENT_DATE之类的函数的值。唯一

  • 问题内容: 有谁知道如何将JS dateTime转换为MySQL datetime?还有一种方法可以向JS日期时间添加特定的分钟数,然后将其传递给MySQL日期时间? 问题答案: 尽管JS确实拥有足够的基本工具来执行此操作,但它相当笨拙。

  • 问题内容: 我的Grails应用程序中有以下代码行,用于将默认时区设置为UTC: 我有一个带有字段的实体: 然后创建并保存一个实例: 这会将它正确地保存为我的数据库(以UTC时间)。但是,当我尝试将其读回时: 时间戳将改为作为本地时间读回。因此,如果我的时区是+1 UTC并且当前本地时间是 12:34:56 BST ,将保存到数据库的时间是 11:34:56 ,但是当我读回它时,它将变成 11:3

  • 问题内容: 我已经尝试过一百万种不同的方法,但是没有用。任何帮助将非常感激。 上面的方法不起作用。 基本上,我想做的是获取纪元时间并将其转换为澳大利亚时间。我的当地时间是+05.30,但是我当然不希望这成为促成这种转化的因素。 编辑- 当我运行您的确切代码时,输​​出 时代1318388699000 2011年10月12日星期三08:34:59 GMT + 05:30 12/10/2011 03:

  • 嗨,我有一个android应用程序,使用返回Int(或Long)类型的时间戳。我想将其转换为人类阅读器日期时间(根据设备时区) 例如,1175714200转换为GMT:2007年4月4日星期三19:16:40 GMT您的时区:2007年4月5日,3:16:40 AM GMT 8:00 我使用这个函数进行转换,但似乎没有返回正确的结果(所有结果都像(15/01/1970 04:04:25),这是不正