JDBC 4.2 和 java.time
Arvind Kumar Avinash 已经很好地解释了偏移的工作原理(XMLGregorianCalendar
误导性地称为时区)。他还推荐了现代 Java 日期和时间 API java.time。我想详细说明这项建议。假设你认为你想要一个java.sql.Timestamp
用于你的 SQL 数据库,你不应该想要那个。从 JDBC 4.2 和 Hibernate 5 开始,我们可以无缝地将 java.time 的类型传输到我们的 SQL 数据库。我建议您改为这样做。
如果在 SQL 中使用带时区的时间戳,请在 Java 中使用 UTC 中的 OffsetDateTime
如果 SQL 中的数据类型是timestamp with time zone
(recommended for timestamps),则将一个OffsetDateTime
从 Java 传输到它。对于大多数数据库引擎来说,timestamp with time zone
实际上意味着UTC 中的时间戳,所以为了清楚起见,我更喜欢传递一个UTC 格式的时间戳OffsetDateTime
。有几种转换方法,每种方法都有其优点和缺点。
1. 通过 GregorianCalendar 转换。
// For demonstration build an XMLGregorianCalenadr equivalent to yours
XMLGregorianCalendar xmlGregorianCalendar = DatatypeFactory.newInstance()
.newXMLGregorianCalendar("2020-10-02T13:07:38-06:00");
// Convert to OffsetDateTime for your SQL database
OffsetDateTime dateTime = xmlGregorianCalendar.toGregorianCalendar()
.toZonedDateTime()
.toOffsetDateTime()
.withOffsetSameInstant(ZoneOffset.UTC);
// Show what we’ve got
System.out.println(dateTime);
输出是:
2020-10-02T19:07:38Z
亲:这是官方的转换。
或者2. 通过 String 转换。
OffsetDateTime dateTime = OffsetDateTime.parse(xmlGregorianCalendar.toString())
.withOffsetSameInstant(ZoneOffset.UTC);
Pro:它很短,我认为它不会给人任何惊喜。缺点:对我来说,格式化为字符串并再次解析回日期时间对象感觉像是一种浪费。
3. 通过 int 转换。您可以通过将各个字段从其中取出XMLGregorianCalendar
并从中构建一个来进行转换OffsetDateTime
。它变得冗长,处理秒的分数有点复杂,并且存在意外交换字段的风险,所以我不推荐它。
如何传递给 SQL。这是一个转移OffsetDateTime
到 SQL 的例子。
String sqlString = "insert into your_table(your_timestamp_column) values (?);";
PreparedStatement ps = yourDatabaseConnection.prepareStatement(sqlString);
ps.setObject(1, dateTime);
int rowsInserted = ps.executeUpdate();
如果在 SQL 中使用没有时区的时间戳,则从 Java 传递 LocalDateTime
如果 SQL 中的数据类型是timestamp
无时区的(不推荐用于时间戳,但经常看到),则需要LocalDateTime
从 Java 传递一个。同样有几种方法可以转换。我只展示一个:
LocalDateTime dateTime = xmlGregorianCalendar.toGregorianCalendar()
.toZonedDateTime()
.withZoneSameInstant(ZoneId.systemDefault())
.toLocalDateTime();
我使用 JVM 的默认时区与老式Timestamp
类所做的一致,因此结果取决于时区。在我的时区(欧洲/哥本哈根)目前在 UTC 偏移 +02:00,结果是:
2020-10-02T21:07:38
你的代码出了什么问题?
您使用的三参数XMLGregorianCalendar.toGregorianCalendar(TimeZone, Locale, XMLGregorianCalendar)
旨在引入与您观察到的错误类似的错误。它承诺GregorianCalendar
使用指定的时区(如果已给出)和来自XMLGregorianCalendar
(年、月、日、小时、分钟等)的字段值来构造 a。所以文档说得很清楚:如果您指定的时区与 的偏移量不一致XMLGregorianCalendar
,它会为您提供与XMLGregorianCalendar
. XMLGregorianCalendar
如果没有任何偏移并且我们知道要使用哪个时区,它有时可能会很有用。但是你XMLGregorianCalendar
有偏移,所以这个方法绝对不是你想要使用的。
链接