Java.sql.Timestamp 是否具有时区特定性?

22 浏览
0 Comments

Java.sql.Timestamp 是否具有时区特定性?

我需要存储UTC日期时间到数据库中。

我将在特定时区给定的日期时间转换为UTC。我遵循以下代码。

我的输入日期时间是“20121225 10:00:00 Z”,时区是“Asia/Calcutta”

我的服务器/数据库(oracle)在相同的时区(IST)“Asia/Calcutta”上运行。

在这个特定的时区获取日期对象

        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()));

存储到数据库中

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

输出

数据库(oracle)存储了相同的给定dateTime:“20121225 10:00:00而不是UTC。

我从下面的sql确认了。

     select to_char(sched_start_time, 'yyyy/mm/dd hh24:mi:ss') from myTable

我的数据库服务器也在相同的时区“Asia/Calcutta”上运行

它给了我以下的外观

  1. Date.getTime()不是UTC
  2. 或时间戳在存储到数据库中时有时区影响

还有一个问题:

timeStamp.toString()会像java.util.date一样以本地时区打印吗?而不是UTC?

admin 更改状态以发布 2023年5月20日
0
0 Comments

我认为正确答案应该是java.sql.Timestamp不具有时区特定性。Timestamp是java.util.Date和单独的纳秒值的组合。此类中没有时区信息。因此,与Date一样,此类仅保持自1970年1月1日00: 00: 00 GMT +纳秒以来的毫秒数。

在PreparedStatement.setTimestamp(int parameterIndex,Timestamp x,Calendar cal)中,Calendar由驱动程序用于更改默认时区。但是Timestamp仍在GMT中保持毫秒。

API不清楚JDBC驱动程序应如何使用日历。提供者似乎可以随意解释它的使用方式,例如最后一次我使用MySQL 5.5日历时,驱动程序在PreparedStatement.setTimestamp和ResultSet.getTimestamp中都忽略了日历。

0
0 Comments

虽然没有明确规定,但是对于 setTimestamp(int parameterIndex, Timestamp x) 方法,驱动程序必须遵循 setTimestamp(int parameterIndex, Timestamp x, Calendar cal) javadoc 中建立的规则:

使用指定的 Calendar 对象将指定的参数设置为给定的 java.sql.Timestamp 值。驱动程序使用 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);

JDBC 4.2 中,符合规范的驱动程序应通过 get/set/updateObject 支持 TIMESTAMP(和 TIME)的 java.time.LocalDateTime(和 java.time.LocalTime)。java.time.Local* 类没有时区,因此不需要应用转换(尽管如果您的代码假定特定时区,那可能会产生一组新的问题)。

0