【问题标题】:How to store ZonedDateTime of java in TIMESTAMPTZ in oracle?如何在oracle的TIMESTAMPTZ中存储java的ZonedDateTime?
【发布时间】:2021-11-27 04:44:40
【问题描述】:

我想将 ZonedDateTime 存储在 oracle 中的 TIMESTAMP WITH TIME ZONE 数据类型中。 如果我尝试将字符串直接存储为字符串,则会抛出无效月份。 然后我发现我可以将它转换为java中的TIMESTAMPTZ然后存储,因为我们需要将字符串转换为TIMESTAMPTZ及其抛出错误。

String d = "2021-10-28 02:36:08.000000 +02:00";
TIMESTAMPTZ t = new TIMESTAMPTZ(con, d);
PreparedStatement ps = con.prepareStatement(query);
ps.setObject(1,t);

错误/堆栈跟踪:

Exception in thread "main" java.lang.IllegalArgumentException: Timestamp format must be yyyy-mm-dd hh:mm:ss[.fffffffff]
  at java.sql.Timestamp.valueOf(Timestamp.java:251)
  at oracle.sql.TIMESTAMPTZ.toBytes(TIMESTAMPTZ.java:1919)
  at oracle.sql.TIMESTAMPTZ.<init>(TIMESTAMPTZ.java:253)
  at OracleSelectQuery.main(OracleSelectQuery.java:21)

请有人调查一下。

【问题讨论】:

  • your previous question 之后,我很想问您是只想保存 UTC 偏移量 (+02:00) 还是需要保存时区(例如欧洲/巴黎)?

标签: java datetime timestamp timestamp-with-timezone zoneddatetime


【解决方案1】:

三参数 TIMESTAMPTZ(Connection, Timestamp, ZoneId) 构造函数

根据 Oracle TIMESTAMPTZ 类的文档(底部的链接),它有一个构造函数,除了连接之外,还需要一个 java.sql.Timestamp 和一个 java.time.ZoneId 作为参数(一个过时的和现代 Java 类)。由于我们可以从您的字符串中提取ZoneOffset,而ZoneOffsetZoneId 的子类,因此我们可以将此构造函数用于您的目的:

    String d = "2021-10-28 02:36:08.000000 +02:00";
    OffsetDateTime odt = OffsetDateTime.parse(d, PARSER);
    Instant inst = odt.toInstant();
    ZoneId offsetAsZoneId = odt.getOffset(); 
    TIMESTAMPTZ t = new TIMESTAMPTZ(con, Timestamp.from(inst), offsetAsZoneId);

我使用这个格式化程序进行解析:

private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()
        .append(DateTimeFormatter.ISO_LOCAL_DATE)
        .appendLiteral(' ')
        .append(DateTimeFormatter.ISO_LOCAL_TIME)
        .appendLiteral(' ')
        .appendOffsetId()
        .toFormatter(Locale.ROOT);

您还可以将时区保存到 Oracle

我使用的构造函数接受ZoneId 开启了额外的可能性,即我们可以将欧洲/巴黎或亚洲/加尔各答等实时时区 ID 存储到数据库中,而不仅仅是裸露的 UTC 偏移量。至少按照我阅读 Oracle 数据库文档的方式,它的 timestamp with time zone 数据类型可以保存时区 ID。文档中给出的示例是 America/Los_Angeles。

关于将ZonedDateTime 转换为TIMESTAMPTZ 的简单示例:

    ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Kolkata"));
    Instant inst = zdt.toInstant();
    ZoneId zid = zdt.getZone(); 
    TIMESTAMPTZ t = new TIMESTAMPTZ(con , Timestamp.from(inst), zid);

链接

【讨论】:

  • 谢谢,问题解决了,我就是这样用的
【解决方案2】:

java.time

following table 描述了 ANSI SQL 类型与java.time 类型的映射:

ANSI SQL Java SE 8
DATE LocalDate
TIME LocalTime
TIMESTAMP LocalDateTime
TIME WITH TIMEZONE OffsetTime
TIMESTAMP WITH TIMEZONE OffsetDateTime

将给定的日期时间字符串解析为OffsetDateTime,如下所示:

import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSSSSS XXX", Locale.ENGLISH);
        String strDateTime = "2021-10-28 02:36:08.000000 +02:00";
        OffsetDateTime odt = OffsetDateTime.parse(strDateTime, dtf);
        System.out.println(odt);
    }
}

输出:

2021-10-28T02:36:08+02:00

ONLINE DEMO

现在,您可以将这个OffsetDateTime 存储到数据库中,如下所示:

PreparedStatement st = conn.prepareStatement("INSERT INTO mytable (columnfoo) VALUES (?)");
st.setObject(1, odt);
st.executeUpdate();
st.close();

通过 Trail: Date Time 了解有关 modern Date-Time API* 的更多信息。


* 如果您正在为一个 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaring。请注意,Android 8.0 Oreo 已经提供了support for java.time

【讨论】:

  • 谢谢,解决了这个问题虽然我使用了一些不同的方式
猜你喜欢
  • 2019-01-17
  • 2017-05-02
  • 2017-12-09
  • 1970-01-01
  • 1970-01-01
  • 2017-10-09
  • 1970-01-01
  • 2020-11-27
  • 1970-01-01
相关资源
最近更新 更多