java.sql.Timestamp 时区是特定的吗?

2022-08-31 10:37:23

我必须将UTC日期时间存储在数据库中。
我已将特定时区中给出的日期时间转换为UTC。为此,我遵循了以下代码。
我的输入日期时间是“20121225 10:00:00 Z”时区是“亚洲/加尔各答”
我的服务器/DB(预言机)在同一时区(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()));

存储到数据库

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

输出

DB(预言机)存储了相同的给定,而不是在UTC中。dateTime: "20121225 10:00:00

我已经从下面的sql中确认。

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

我的数据库服务器也在同一时区“亚洲/加尔各答”上运行

它给了我以下外观

  1. Date.getTime()不在 UTC 中
  2. 或者时间戳在存储到数据库时具有时区影响 我在这里做错了什么?

还有一个问题:

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


答案 1

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

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

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

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

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

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

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
stmt.setTimestamp(11, tsSchedStartTime, cal);

在 JDBC 4.2 中,兼容的驱动程序应支持 (and) 到 。。这些类没有时区,因此不需要应用转换(尽管如果您的代码确实假定了特定的时区,这可能会带来一系列新的问题)。java.time.LocalDateTimejava.time.LocalTimeTIMESTAMPTIMEget/set/updateObjectjava.time.Local*


答案 2

我认为正确的答案应该是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驱动程序应该如何使用日历。提供商似乎对如何解释它感到随意,例如,上次我使用MySQL 5.5 Calendar时,驱动程序只是忽略了PredentStatement.setTimestamp和ResultSet.getTimestamp中的Calendar。