java.sql.timestamp时区特定吗?

eni9jsuy  于 2021-06-10  发布在  Hbase
关注(0)|答案(6)|浏览(1040)

我必须在数据库中存储utc日期时间。
我已将特定时区中给定的日期时间转换为utc。为此,我遵循以下代码。
我的输入日期时间是“20121225 10:00:00 z”时区是“亚洲/加尔各答”
我的服务器/数据库(oracle)在同一时区(ist)“亚洲/加尔各答”运行
获取此特定时区中的日期对象

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(oracle)存储了相同的 dateTime: "20121225 10:00:00 不是utc。
我已经从下面的sql中确认了。

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

我的数据库服务器也运行在同一时区“亚洲/加尔各答”
它给了我以下的外观 Date.getTime() 不是utc格式
或者timestamp在存储到db时有时区影响,我在这里做错了什么?
还有一个问题:
威尔 timeStamp.toString() 按本地时区打印 java.util.date 做?不是utc?

0lvr5msh

0lvr5msh1#

答案是 java.sql.Timestamp 是一团乱,应该避免。使用 java.time.LocalDateTime 相反。
那为什么会一团糟呢?从 java.sql.Timestamp javadoc公司 java.sql.Timestamp 是个“薄薄的包裹” java.util.Date 允许JDBCAPI将其标识为sql时间戳值”。从 java.util.Date javadoc,“这个 Date 类旨在反映协调世界时(utc)”。在isosql规范中,不带时区的timestamp“是一种不带时区的datetime数据类型”。timestamp是没有时区的timestamp的简称。所以 java.sql.Timestamp “反映”utc,而sql时间戳为“无时区”。
因为 java.sql.Timestamp 它的方法应用转换。这引起了无尽的混乱。从sql的Angular 来看,将sql时间戳值转换为其他时区是没有意义的,因为时间戳没有可转换的时区。把42转成华氏度是什么意思?它没有任何意义,因为42没有温度单位。只是个空号。同样,您不能将时间戳2020-07-22t10:38:00转换为美洲/洛杉矶,因为2020-07-22t10:30:00不在任何时区。它不是在utc或gmt或其他任何时间。这是一个赤裸裸的约会时间。 java.time.LocalDateTime 也是一个光鲜的约会时间。它没有时区,就像sql时间戳一样。它没有一种方法应用任何类型的时区转换,这使得它的行为更容易预测和理解。所以不要使用 java.sql.Timestamp . 使用 java.time.LocalDateTime .

LocalDateTime ldt = rs.getObject(col, LocalDateTime.class);
ps.setObject(param, ldt, JDBCType.TIMESTAMP);
kuhbmx9i

kuhbmx9i2#

对于mysql,我们有一个限制。在驱动程序mysql doc中,我们有:
以下是mysql connector/j的一些已知问题和限制:当connector/j在结果集中使用gettimestamp()方法检索夏时制(dst)切换日的时间戳时,返回的一些值可能是错误的。连接到数据库时,可以使用以下连接选项来避免错误:

useTimezone=true
useLegacyDatetimeCode=false
serverTimezone=UTC

所以,当我们不使用这个参数,我们调用 setTimestamp or getTimestamp 无论是否使用日历,我们都在jvm时区中有时间戳。
例子:
jvm时区是gmt+2。在数据库中,我们有一个时间戳:1461100256=19/04/16 21:10:56000000000 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

第一个方法返回: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指定参数。

unguejic

unguejic3#

虽然没有明确规定 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) 具有所需时区中的日历示例。只要确保在检索值时也使用具有相同时区的等效getter(如果使用 TIMESTAMP 数据库中没有时区信息)。
因此,假设要存储实际的gmt时区,则需要使用:

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

对于JDBC4.2,兼容的驱动程序应该支持 java.time.LocalDateTime (和 java.time.LocalTime )为了 TIMESTAMP (和 TIME )通过 get/set/updateObject . 这个 java.time.Local* 类没有时区,因此不需要应用任何转换(尽管如果您的代码假定了特定的时区,这可能会带来一组新的问题)。

2nbm6dog

2nbm6dog4#

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

ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Calcutta")) ;
Timestamp timestamp = Timestamp.valueOf(zdt.toLocalDateTime());

人们常犯的一个错误就是使用 LocaleDateTime 获取该时刻的时间戳,该时刻将丢弃指定给区域的任何信息,即使您稍后尝试将其转换。它不了解这个区域。
请注意 Timestamp 他是班里的一员 java.sql.Timestamp .

ybzsozfc

ybzsozfc5#

我认为正确的答案应该是java.sql.timestamp不是特定于时区的。timestamp是java.util.date和一个单独的纳秒值的组合。此类中没有时区信息。因此,就像date一样,这个类只保存自1970年1月1日00:00:00GMT+nanos以来的毫秒数。
在preparedstatement.settimestamp(int parameterindex,timestamp x,calendar cal)中,驱动程序使用日历更改默认时区。但时间戳仍然以毫秒为单位。
api不清楚jdbc驱动程序到底应该如何使用日历。提供者似乎对如何解释它感到很自在,例如,上次我使用mysql 5.5 calendar时,驱动程序在preparedstatement.settimestamp和resultset.gettimestamp中都忽略了calendar。

bejyjqdl

bejyjqdl6#

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

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

进一步说明:

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

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

相关问题