关于oracle:java.sql.Timestamp时区是否具体?

Is java.sql.Timestamp timezone specific?

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

获取此特定时区中的日期对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        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()));

存储到数据库中

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

产量

DB(Oracle)已存储相同的给定dateTime:"20121225 10:00:00,而不是UTC。

我已从下面的SQL中确认。

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

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

它给了我下面的样子

  • Date.getTime()不在UTC中
  • 或者时间戳在存储到数据库时具有时区影响我在这里做错什么了?
  • 还有一个问题:

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


    虽然没有明确规定用于setTimestamp(int parameterIndex, Timestamp x)驱动程序,但必须遵循setTimestamp(int parameterIndex, Timestamp x, Calendar cal)javadoc制定的规则:

    Sets the designated parameter to the given java.sql.Timestamp value, using the given Calendar object. The driver uses the Calendar object to construct an SQL TIMESTAMP value, which the driver then sends to the database. With a Calendar object, the driver can calculate the timestamp taking into account a custom time zone. If no Calendar object is specified, the driver uses the default time zone, which is that of the virtual machine running the application.

    当使用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)与所需时区中的日历实例一起使用。只需确保在检索值时使用具有相同时区的等效getter(如果使用的TIMESTAMP在数据库中没有时区信息)。

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

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

    对于JDBC 4.2,兼容驱动程序应支持java.time.LocalDateTimejava.time.LocalTime,通过get/set/updateObject支持TIMESTAMPTIMEjava.time.Local*类没有时区,因此不需要应用转换(尽管如果代码假定了一个特定的时区,这可能会打开一组新的问题)。


    我认为正确的答案应该是java.sql.timestamp不是特定于时区的。timestamp是java.util.date和单独纳秒值的组合。此类中没有时区信息。因此,就像日期一样,这个类只保存了1970年1月1日00:00:00 gmt+nanos以来的毫秒数。

    在PreparedStatement.SetTimeStamp中(int参数index,时间戳x,日历cal)驱动程序使用日历更改默认时区。但时间戳在格林尼治标准时间内仍保持毫秒。

    API不清楚JDBC驱动程序应该如何使用日历。提供程序似乎对如何解释它感到自由,例如,上次我使用MySQL5.5日历时,驱动程序只是忽略了PreparedStatement.SetTimeStamp和ResultSet.GetTimeStamp中的日历。


    这是你的司机说的。您需要在Java程序中提供一个参数来告诉它要使用的时区。

    1
    java -Duser.timezone="America/New_York" GetCurrentDateTimeZone

    此外:

    1
    to_char(new_time(sched_start_time, 'CURRENT_TIMEZONE', 'NEW_TIMEZONE'), 'MM/DD/YY HH:MI AM')

    在正确处理转换时也可能有价值。从这里夺走


    对于MySQL,我们有一个限制。在驱动程序mysql文件中,我们有:

    The following are some known issues and limitations for MySQL
    Connector/J: When Connector/J retrieves timestamps for a daylight
    saving time (DST) switch day using the getTimeStamp() method on the
    result set, some of the returned values might be wrong. The errors can
    be avoided by using the following connection options when connecting
    to a database:

    1
    2
    3
    useTimezone=true
    useLegacyDatetimeCode=false
    serverTimezone=UTC

    所以,当我们不使用这个参数,我们称之为setTimestamp or getTimestamp,有日历或没有日历,我们在jvm时区中有时间戳。

    例子:

    JVM时区是GMT+2。在数据库中,我们有一个时间戳:146110256=19/04/16 21:10:56000000000 GMT

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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

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

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

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

    与Oracle不同,当我们使用相同的调用时,我们有:

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

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

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

    NB:不需要为Oracle指定参数。