Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/hibernate/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
带时区的Java和Oracle时间戳_Oracle_Hibernate_Timestamp_Ojdbc_Offsetdatetime - Fatal编程技术网

带时区的Java和Oracle时间戳

带时区的Java和Oracle时间戳,oracle,hibernate,timestamp,ojdbc,offsetdatetime,Oracle,Hibernate,Timestamp,Ojdbc,Offsetdatetime,我正试图用hibernate在Oracle中保存两个日期。这两个日期在莫斯科时区具有相同的时间戳:2005-10-30T02:00+03:00[欧洲/莫斯科]和2005-10-30T02:00+04:00[欧洲/莫斯科](“2005年10月30日星期日02:00:00 MSK”和“2005年10月30日星期日02:00:00 MSD”)。日期的时间间隔为一小时,并与冬季/夏季时间的过渡相关 我在Oracle中创建了表: create table TMP ( ID LONG,

我正试图用hibernate在Oracle中保存两个日期。这两个日期在莫斯科时区具有相同的时间戳:2005-10-30T02:00+03:00[欧洲/莫斯科]和2005-10-30T02:00+04:00[欧洲/莫斯科](“2005年10月30日星期日02:00:00 MSK”和“2005年10月30日星期日02:00:00 MSD”)。日期的时间间隔为一小时,并与冬季/夏季时间的过渡相关

我在Oracle中创建了表:

create table TMP
(
    ID    LONG,
    TS    TIMESTAMP,
    TSLTZ TIMESTAMP WITH LOCAL TIME ZONE,
    TSTZ  TIMESTAMP WITH TIME ZONE
);
我的模块中的实体:

@Entity
@Table(name = "tmp")
public class DateTimeOracle {

    private Long id;
    private ZonedDateTime ts;
    private ZonedDateTime tsltz;
    private ZonedDateTime tstz;

    @Id
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public ZonedDateTime getTs() {
        return ts;
    }

    public ZonedDateTime setTs(ZonedDateTime ts) {
        this.ts = ts;
    }

    public ZonedDateTime getTsltz() {
        return tsltz;
    }

    public ZonedDateTime setTsltz(ZonedDateTime tsltz) {
        this.tsltz = tsltz;
    }

    public ZonedDateTime getTstz() {
        return tstz;
    }

    public ZonedDateTime setTstz1(ZonedDateTime tstz) {
        this.tstz = tstz;
    }
}
在实体中,所有字段都由一个日期初始化。 保存后,Oracle中的两个日期具有相同的值,如下所示:

ts=2005-10-3002:00:00.000000

TSLTZ=2005-10-29 23:00:00.000000

TSTZ=2005-10-30 02:00:00.000000+04:00

为什么oracle在不同的日期(包括偏移量+04:00)保持相同的值?有办法解决这个问题吗

p.S.Postgres正确存储日期。一个偏移量为+03:00,另一个偏移量为+04:00(分别为2005-10-29 23:00:00.000000和2005-10-29 22:00:00.000000)

更新

以下是我创建日期的方式:

Date dt2 = new Date(1130623200000L); //2005-10-29 23:00:00 +04:00
Date dt3 = new Date(1130626800000L); //2005-10-29 23:00:00 +03:00
ZonedDateTime zdt2 = ZonedDateTime.ofInstant(dt2.toInstant(), ZoneId.systemDefault()); // My zone is MSK
ZonedDateTime zdt3 = ZonedDateTime.ofInstant(dt3.toInstant(), ZoneId.systemDefault()); // My zone is MSK
OffsetDateTime odt2 = zdt2.toOffsetDateTime();
OffsetDateTime odt3 = zdt3.toOffsetDateTime();
如果我不使用Hibernate并直接使用jdbc,情况不会改变

Connection conn = DriverManager.getConnection("<oracle_url>",
                        "<username>", "<password>");
PreparedStatement pstmt = conn.prepareStatement("insert into tmp (id, TSTZ1, TSTZ2) values (200, ?, ?)", Statement.RETURN_GENERATED_KEYS);
pstmt.setDate(1, new java.sql.Date(dt2.getTime()));
pstmt.setDate(2, new java.sql.Date(dt3.getTime()));
int z1 = pstmt.executeUpdate();
pstmt.close();
conn.close();
我在DB中看到:

2005-10-29 22:00:00.000000  2005-10-29 23:00:00.000000  2005-10-30 02:00:00.000000 +04:00   2005-10-30 02:00:00.000000 +03:00
但是,如果我保存ZonedDateTime,则带有本地时区的时间戳中的值是正确的,而带有时区的时间戳中的值是不正确的

PreparedStatement pstmt = conn.prepareStatement("insert into tmp (TSLTZ1, TSLTZ2, TSTZ1, TSTZ2) values (?, ?, ?, ?)");    
pstmt.setObject(1, zdt2);
pstmt.setObject(2, zdt3);
pstmt.setObject(3, zdt2);
pstmt.setObject(4, zdt3);
在DB中,我看到:

2005-10-29 22:00:00.000000  2005-10-29 23:00:00.000000  2005-10-30 02:00:00.000000 +04:00   2005-10-30 02:00:00.000000 +04:00

最后两个值不正确。

有关Oracle时间戳数据类型的一些解释:

PreparedStatement pstmt = conn.prepareStatement("insert into tmp (TSLTZ1, TSLTZ2, TSTZ1, TSTZ2) values (?, ?, ?, ?)");    
pstmt.setObject(1, zdt2);
pstmt.setObject(2, zdt3);
pstmt.setObject(3, zdt2);
pstmt.setObject(4, zdt3);
  • 时间戳
    :不存储任何时区信息。如果输入带有时区的时间戳,则时区信息将被截断并丢失

  • 带时区的时间戳
    :将时间戳插入数据库时,存储带时区信息的时间戳(即作为命名区域或UTC偏移量)

  • 带有本地时区的时间戳
    :时间戳存储为
    DBTIMEZONE
    (推荐,通常为
    UTC
    )。时间戳始终且仅在当前用户会话中显示。因此,它不显示任何时区信息,因为根据定义,这始终是您的本地时区

我应该用哪一个

当然,这取决于你的要求

使用带有本地时区的
时间戳
您不必关心客户端的任何设置,时间始终显示为本地时间。时间存储在
DBTIMEZONE
中,因此您将丢失原始插入的时区

请注意,当您在带有时区的
时间戳上创建索引时。无法直接在此类列上创建索引。相反,Oracle为
SYS\u EXTRACT\u UTC(TSTZ)
创建一个虚拟列,并在此虚拟列上创建索引。在开发查询时,应该注意这一点

更新

你的处境很特殊。当您插入时间戳“2005-10-30 02:00:00 Europe/Moscow”时
则此时间不明确,它可能表示
2005-10-30 02:00:00+03:00
2005-10-30 02:00+04:00

举个例子:

SELECT TO_CHAR(TIMESTAMP '2005-10-30 00:00:00 Europe/Moscow' + LEVEL * INTERVAL '1' HOUR, 
    'YYYY-MM-DD hh24:mi:ss TZH:TZM TZD tzr') AS ts
FROM dual
CONNECT BY LEVEL <= 4;

+--------------------------------------------+
|TS                                          |
+--------------------------------------------+
|2005-10-30 01:00:00 +04:00 MSD Europe/Moscow|
|2005-10-30 02:00:00 +04:00 MSD Europe/Moscow|
|2005-10-30 02:00:00 +03:00 MSK Europe/Moscow|
|2005-10-30 03:00:00 +03:00 MSK Europe/Moscow|
+--------------------------------------------+
如果未添加TZD format元素,并且datetime值不明确,则如果将
error\u ON\u OVERLAP\u TIME
会话参数设置为
TRUE
,则Oracle数据库将返回一个错误。如果将时间重叠上的
ERROR\u设置为
FALSE
(默认值),则Oracle数据库将不明确的日期时间解释为标准时间

注意,时区
+04:00
+03:00
不等于
欧洲/莫斯科
。时区
欧洲/莫斯科
考虑夏令时(大约10年前在俄罗斯仍然使用),但
+04:00
/
+03:00
不考虑

对不起,我从未使用过hibernate,所以我不知道这个框架如何处理这些数据。我也不熟悉Java。可能不支持夏令时信息

我可以猜测,class
java.sql.Date
和method
setDate
指的是Oracle中的
Date
数据类型。如前所述,最好使用
java.sql.Timestamp
setTimestamp

DATE
数据类型不支持任何时区信息。如果您尝试将
日期
值插入到带有[本地]时区的
时间戳
列中,那么Oracle实际上会这样做

FROM_TZ(CAST(<your DATE value> AS TIMESTAMP), SESSIONTIMEZONE)
FROM_TZ(CAST(作为时间戳),SESSIONTIMEZONE)

您的条件非常特殊,让我们试着减轻一点

Unix时间1130623200是UTC标准时间2005-10-29 22:00:00

  • 莫斯科时间是2005-10-30 02:00:00 Europe/Moscow
,但这是不明确的。 可能是
  • 欧洲/莫斯科MSD
    2005-10-30 02:00:00+04:00
  • 2005-10-30 02:00:00+03:00欧洲/莫斯科MSK
  • Unix时间1130626800是
    2005-10-29 23:00:00 UTC

    • 莫斯科时间是2005-10-30 02:00:00 Europe/Moscow,但这是不明确的。 可能是
    • 欧洲/莫斯科MSD
      2005-10-30 02:00:00+04:00
    • 2005-10-30 02:00:00+03:00欧洲/莫斯科MSK
    根据Oracle文档,不明确的
    时间戳“2005-10-30 02:00:00欧洲/莫斯科”
    改为标准时间,即
    2005-10-30 02:00:00欧洲/莫斯科MSK+03:00
    (与2005年一样!)

    请注意,2005年莫斯科的标准时间是
    MSK=>+03:00
    。2011年,俄罗斯政府宣布,未来全年都将使用夏时制,从而有效地取代了标准时间。也就是说,今天莫斯科的标准时间是
    MSK=>+04:00
    ,在2011年之前被称为
    MSD

    核实

    SELECT 
        TO_CHAR(TIMESTAMP '2005-10-29 22:00:00 UTC' AT TIME ZONE 'Europe/Moscow', 'YYYY-MM-DD HH24:MI:SS TZH:TZM tzr TZD') AS TS_1_UTC,
        TO_CHAR(TIMESTAMP '2005-10-29 23:00:00 UTC' AT TIME ZONE 'Europe/Moscow', 'YYYY-MM-DD HH24:MI:SS TZH:TZM tzr TZD') AS TS_2_UTC,
        TO_CHAR(TIMESTAMP '2005-10-30 02:00:00 Europe/Moscow', 'YYYY-MM-DD HH24:MI:SS TZH:TZM tzr TZD') AS TS,
        TO_CHAR(TIMESTAMP '2005-10-30 02:00:00 Europe/Moscow MSK', 'YYYY-MM-DD HH24:MI:SS TZH:TZM tzr TZD') AS TS_MSK,
        TO_CHAR(TIMESTAMP '2005-10-30 02:00:00 Europe/Moscow MSD', 'YYYY-MM-DD HH24:MI:SS TZH:TZM tzr TZD') AS TS_MSD
    FROM dual
    
    世界协调时1时 世界协调时2时 TS 苏木斯克 TS_MSD 2005-10-30 02:00:
    SELECT 
        TO_CHAR(TIMESTAMP '2005-10-29 22:00:00 UTC' AT TIME ZONE 'Europe/Moscow', 'YYYY-MM-DD HH24:MI:SS TZH:TZM tzr TZD') AS TS_1_UTC,
        TO_CHAR(TIMESTAMP '2005-10-29 23:00:00 UTC' AT TIME ZONE 'Europe/Moscow', 'YYYY-MM-DD HH24:MI:SS TZH:TZM tzr TZD') AS TS_2_UTC,
        TO_CHAR(TIMESTAMP '2005-10-30 02:00:00 Europe/Moscow', 'YYYY-MM-DD HH24:MI:SS TZH:TZM tzr TZD') AS TS,
        TO_CHAR(TIMESTAMP '2005-10-30 02:00:00 Europe/Moscow MSK', 'YYYY-MM-DD HH24:MI:SS TZH:TZM tzr TZD') AS TS_MSK,
        TO_CHAR(TIMESTAMP '2005-10-30 02:00:00 Europe/Moscow MSD', 'YYYY-MM-DD HH24:MI:SS TZH:TZM tzr TZD') AS TS_MSD
    FROM dual