java.sql.Timestamp时区是否具体?

Kan*_*mar 74 java oracle datetime date jdbc

我必须在UTC中存储UTC dateTime.
我已将特定时区中给出的dateTime转换为UTC.因为我遵循以下代码.
我的输入日期时间是"20121225 10:00:00 Z"时区是"亚洲/加尔各答"
我的服务器/数据库(oracle)在同一时区(IST)"亚洲/加尔各答"运行

获取此特定时区中的Date对象

        String date = "20121225 10:00:00 Z";
        String timeZoneId = "Asia/Calcutta";
        TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);

        DateFormat dateFormatLocal = new SimpleDateFormat("yyyyMMdd HH:mm:ss z");
                    //This date object is given time and given timezone
        java.util.Date parsedDate = dateFormatLocal.parse(date + " "  
                         + timeZone.getDisplayName(false, TimeZone.SHORT));

        if (timeZone.inDaylightTime(parsedDate)) {
            // We need to re-parse because we don't know if the date
            // is DST until it is parsed...
            parsedDate = dateFormatLocal.parse(date + " "
                    + timeZone.getDisplayName(true, TimeZone.SHORT));
        }

       //assigning to the java.sql.TimeStamp instace variable
        obj.setTsSchedStartTime(new java.sql.Timestamp(parsedDate.getTime()));
Run Code Online (Sandbox Code Playgroud)

存入DB

        if (tsSchedStartTime != null) {
            stmt.setTimestamp(11, tsSchedStartTime);
        } else {
            stmt.setNull(11, java.sql.Types.DATE);
        }
Run Code Online (Sandbox Code Playgroud)

OUTPUT

DB(oracle)存储了相同的给定dateTime: "20121225 10:00:00而不是UTC.

我已从下面的sql确认.

     select to_char(sched_start_time, 'yyyy/mm/dd hh24:mi:ss') from myTable
Run Code Online (Sandbox Code Playgroud)

我的数据库服务器也运行在同一时区"Asia/Calcutta"

它给了我以下的外观

  1. Date.getTime() 不是UTC
  2. 或者时间戳在存入DB时会产生时区影响我在这里做错了什么?

还有一个问题:

timeStamp.toString()在当地时区打印java.util.date吗?不是UTC?

Mar*_*eel 95

虽然未明确指定setTimestamp(int parameterIndex, Timestamp x)驱动程序必须遵循setTimestamp(int parameterIndex, Timestamp x, Calendar cal)javadoc建立的规则:

java.sql.Timestamp使用给定Calendar对象将指定参数设置为给定值.驱动程序使用该Calendar对象构造一个SQL TIMESTAMP值,然后驱动程序将其发送到数据库.使用Calendar对象,驱动程序可以计算考虑自定义时区的时间戳.如果未Calendar指定任何对象,则驱动程序将使用默认时区,即运行应用程序的虚拟机的时区.

setTimestamp(int parameterIndex, Timestamp x)使用JDBC驱动程序调用时,使用虚拟机的时区来计算该时区中时间戳的日期和时间.此日期和时间是存储在数据库中的日期和时间,如果数据库列不存储时区信息,则有关该区域的任何信息都将丢失(这意味着应由使用数据库的应用程序来使用同一时区一致或提出另一种方案来辨别时区(即存储在一个单独的栏目中).

例如:您当地的时区是GMT + 2.您存储"2012-12-25 10:00:00 UTC".存储在数据库中的实际值是"2012-12-25 12:00:00".您再次检索它:您再次将其作为"2012-12-25 10:00:00 UTC"(但仅在您使用getTimestamp(..)时检索它),但当另一个应用程序在时区GMT + 0中访问数据库时,它将检索时间戳为"2012-12-25 12:00:00 UTC".

如果要将其存储在不同的时区,则需要setTimestamp(int parameterIndex, Timestamp x, Calendar cal)在所需的时区中使用带有Calendar的实例.只需确保在检索值时也使用具有相同时区的等效getter(如果TIMESTAMP在数据库中使用不带时区信息).

因此,假设您要存储实际的GMT时区,则需要使用:

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
stmt.setTimestamp(11, tsSchedStartTime, cal);
Run Code Online (Sandbox Code Playgroud)

使用JDBC 4.2,兼容的驱动程序应支持java.time.LocalDateTime(和java.time.LocalTime)for TIMESTAMP(和TIME)through get/set/updateObject.这些java.time.Local*类没有时区,因此不需要应用任何转换(尽管如果您的代码确实假定了特定的时区,这可能会打开一组新的问题).

  • `java.lang.Date`(和`java.lang.Timestamp`)上的`toString()`将始终在您的本地时区中显示它. (4认同)

Evg*_*eev 31

我认为正确答案应该是java.sql.Timestamp不是特定于时区的.时间戳是java.util.Date和单独的纳秒值的组合.此课程中没有时区信息.因此就像Date一样,这个类只保留自1970年1月1日00:00:00 GMT + nanos以来的毫秒数.

在PreparedStatement.setTimestamp(int parameterIndex,Timestamp x,Calendar cal)中,驱动程序使用日历来更改默认时区.但是时间戳在GMT中仍然保持毫秒.

API不清楚JDBC驱动程序应该如何使用Calendar.提供者似乎对如何解释它感到自由,例如,上次我使用MySQL 5.5 Calendar时,驱动程序只是在PreparedStatement.setTimestamp和ResultSet.getTimestamp中忽略了Calendar.

  • 我认为从 java 8+ 开始这是错误的。在时间戳内部(因为它是从 java.util.Date 扩展而来的)有一个日历属性,它有一个 zoneinfo 属性,实际上包含时区。您仍然可以使用 .getTime() 获取 UTC 时间,因为时间戳也存储它,但它具有时区信息。 (3认同)

Ayu*_*mar 9

您可以使用以下方法将时间戳存储在特定于您所需区域/区域 ID 的数据库中。

ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Calcutta")) ;
Timestamp timestamp = Timestamp.valueOf(zdt.toLocalDateTime());
Run Code Online (Sandbox Code Playgroud)

人们常犯的一个错误是用来LocaleDateTime获取该时刻的时间戳,这会丢弃特定于您的区域的任何信息,即使您稍后尝试转换它也是如此。它不理解该区域。

请注意Timestamp是班级java.sql.Timestamp


小智 9

答案是这java.sql.Timestamp是一团糟,应该避免。使用java.time.LocalDateTime来代替。

那么为什么会一团糟呢?在java.sql.TimestampJavaDoc 中, ajava.sql.Timestamp是一个“瘦包装器java.util.Date,允许 JDBC API 将其标识为 SQL TIMESTAMP 值”。在java.util.DateJavaDoc 中,“Date该类旨在反映协调世界时 (UTC)”。从 ISO SQL 规范中,TIMESTAMP WITHOUT TIME ZONE“是一种没有时区的日期时间数据类型”。TIMESTAMP 是 TIMESTAMP WITHOUT TIME ZONE 的简称。所以java.sql.Timestamp“反映”UTC 而SQL TIMESTAMP 是“没有时区”。

因为java.sql.Timestamp反映了UTC,它的方法应用了转换。这会导致无休止的混乱。从 SQL 的角度来看,将 SQL TIMESTAMP 值转换为其他时区是没有意义的,因为 TIMESTAMP 没有要转换的时区。将 42 转换为华氏度是什么意思?这没有任何意义,因为 42 没有温度单位。这只是一个空洞的数字。同样,您无法将 2020-07-22T10:38:00 的 TIMESTAMP 转换为美洲/洛杉矶,因为 2020-07-22T10:30:00 不在任何时区。它不是 UTC 或 GMT 或其他任何东西。这是一个裸露的约会时间。

java.time.LocalDateTime也是一个裸约会时间。它没有时区,与 SQL TIMESTAMP 完全一样。它的方法都没有应用任何类型的时区转换,这使得它的行为更容易预测和理解。所以不要使用java.sql.Timestamp. 使用java.time.LocalDateTime.

LocalDateTime ldt = rs.getObject(col, LocalDateTime.class);
ps.setObject(param, ldt, JDBCType.TIMESTAMP);
Run Code Online (Sandbox Code Playgroud)


Woo*_*Moo 5

具体来自您的司机.您需要在Java程序中提供一个参数,以告诉它您要使用的时区.

java -Duser.timezone="America/New_York" GetCurrentDateTimeZone
Run Code Online (Sandbox Code Playgroud)

进一步:

to_char(new_time(sched_start_time, 'CURRENT_TIMEZONE', 'NEW_TIMEZONE'), 'MM/DD/YY HH:MI AM')
Run Code Online (Sandbox Code Playgroud)

在正确处理转换方面也可能有价值.取自这里


Abd*_*fid 5

对于Mysql,我们有一个限制。在驱动程序Mysql doc中,我们有:

以下是MySQL Connector / J的一些已知问题和局限性:当Connector / J使用结果集上的getTimeStamp()方法检索夏令时(DST)转换日的时间戳时,某些返回值可能是错误的。连接到数据库时,可以通过使用以下连接选项来避免错误:

useTimezone=true
useLegacyDatetimeCode=false
serverTimezone=UTC
Run Code Online (Sandbox Code Playgroud)

因此,当我们不使用此参数而setTimestamp or getTimestamp使用日历或不使用日历进行调用时,我们将在jvm时区中添加时间戳。

范例:

jvm时区为GMT + 2。在数据库中,我们有一个时间戳:1461100256 = 19/04/16 21:10:56,000000000 GMT

Properties props = new Properties();
props.setProperty("user", "root");
props.setProperty("password", "");
props.setProperty("useTimezone", "true");
props.setProperty("useLegacyDatetimeCode", "false");
props.setProperty("serverTimezone", "UTC");
Connection con = DriverManager.getConnection(conString, props);
......
Calendar nowGMT = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
Calendar nowGMTPlus4 = Calendar.getInstance(TimeZone.getTimeZone("GMT+4"));
......
rs.getTimestamp("timestampColumn");//Oracle driver convert date to jvm timezone and Mysql convert date to GMT (specified in the parameter)
rs.getTimestamp("timestampColumn", nowGMT);//convert date to GMT 
rs.getTimestamp("timestampColumn", nowGMTPlus4);//convert date to GMT+4 timezone
Run Code Online (Sandbox Code Playgroud)

第一种方法返回:1461100256000 = 19/04/2016-21 : 10:56 GMT

第二种方法返回:1461100256000 = 19/04/2016-21 : 10:56 GMT

第三种方法返回:1461085856000 = 19/04/2016-17 : 10:56 GMT

当我们使用相同的调用时,我们使用的不是Oracle,而是:

第一种方法返回:1461093056000 = 19/04/2016-19 : 10:56 GMT

第二种方法返回:1461100256000 = 19/04/2016-21 : 10:56 GMT

第三种方法返回:1461085856000 = 19/04/2016-17 : 10:56 GMT

注意: 不必为Oracle指定参数。

  • 我想您可以为 oracle 将会话时区设置为 UTC,以实现与 MySQL 对于非日历情况相同的转换。“`ALTER SESSION SET TIME_ZONE = 'UTC'`”。 (2认同)