【问题标题】:XMLGregorianCalendar to java.sql.TimestampXMLGregorianCalendar 到 java.sql.Timestamp
【发布时间】:2012-09-24 15:04:15
【问题描述】:

我正在使用 Web 服务,插入记录,返回 XMLGregorianCalendar 类型的时间戳值。我需要将其转换为 java.sql.Timestamp 值,所以我使用了这样的函数。

public static java.sql.Timestamp getSqlTimeStamp(XMLGregorianCalendar xgc) {
    if (xgc == null) {
        return null;
    } else {
        return new Timestamp(xgc.toGregorianCalendar().getTime().getTime());
    }
}
Timestamp timestamp=getSqlTimeStamp(ExitInXMLGregor.getTimestamp());

我的问题是在服务器中,我插入记录时的时间戳值是这样的:2012-10-03T19:23:22.342+02:00

但是当我进行类型转换时,我得到的时间戳值是这样的:2012-10-03T17:23:22.342

服务器中的时间(webservice 所在的位置)比我的语言环境多 2 小时,并且由于某种原因,我在转换后获得了插入语言环境的时间。 问题是我确实需要获取服务器时间,因为在数据库中,时间戳值与服务器一匹配,并且由于时间戳值不同,我在更新操作时遇到了问题。

请,我将不胜感激任何形式的帮助。谢谢!

编辑: 我有点找到解决方案,但不完全是我需要的。当我将 java.sql.Timestamp 格式的时间戳转换为 XMLGregorian 时,我设置了服务器的时区 (setTimeZone(TimeZone.getTimeZone("GMT+02:00")))。这实际上可行,但与理想的解决方案相去甚远(时区甚至服务器可能会发生变化)在这一点上知道服务器的时区会很棒,但我不知道如何...... .

public static XMLGregorianCalendar getXMLGregorianCalendar(Timestamp timestamp)
        throws BaseException {
    try {
        GregorianCalendar gc = new GregorianCalendar();
        gc.setTimeZone(TimeZone.getTimeZone("GMT+02:00"));
        gc.setTimeInMillis(timestamp.getTime());
        return DatatypeFactory.newInstance().newXMLGregorianCalendar(gc);
    } catch (DatatypeConfigurationException ex) {
        throw new BaseException(ex);
    }
}

【问题讨论】:

  • 使用另一个 toGregorianCalendar 方法,您可以在其中指定时区、区域设置和其他 XMLGregorianCalendar 默认值。
  • 问题是,我怎么知道服务器的时区和语言环境。如果服务器更改时区或位置怎么办?
  • 您可以通过向服务器询问当前时间戳来确定服务器的时区。我的猜测是,根据您问题中的日期,服务器距您以西 2 个时区。我不知道这是否对您有帮助,但出于这个原因,服务器上的时间戳应始终使用格林威治标准时间 (GMT) 时区存储。

标签: java spring timestamp gregorian-calendar


【解决方案1】:

我怀疑时间戳确实指定了正确的时间,只是没有显示正确的时区。 2012-10-03T17:23:22.342 与 2012-10-03T19:23:22.342+02:00 相同,假设前者的(隐藏)时区为 +00:00。

【讨论】:

  • 我也很确定,但问题是这个时间 (2012-10-03T17:23:22.342) 值与数据库时间戳 (2012-10- 03T19:23:22.342)。问题是……我该如何解决?
  • 一个 java.sql.Timestamp 代表一个瞬间。它实际上根本不知道时区。它的默认字符串显示在错误的时区这一事实不会影响它的许多用途。您提到“更新操作中的问题”。究竟出了什么问题?您可以使用不符合预期的时间戳发布代码吗?
  • 这个问题很容易理解。当我插入一些东西时,网络服务会返回新记录的 id 和时间戳(在 XMLGregorianCalendar 类型中)。之后,我尝试更新相同的记录,所以我需要传递带有 Id 和时间戳的 bean,但显然我拥有的时间戳和 BD 中的时间戳不匹配。
  • 但是......他们确实匹配。您拥有的时间戳 (2012-10-03T17:23:22.342+00:00) 与记录中的时间戳 (2012-10-03T19:23:22.342+02:00) 在同一时刻。不是服务器处理 2012-10-03T17:23:22.342+00:00 和 2012-10-03T19:23:22.342+02:00 和 2012-10-04T01:23:22.342+07:00等等相同?如果没有,并且服务器无法修复,您可能需要考虑让您的客户端将“时间戳”存储为字符串而不是即时时间。
  • 你又是对的。我遇到了其他问题,并认为时间戳是原因,但事实并非如此。非常感谢,不便之处敬请见谅。
【解决方案2】:

tl;博士

  • 两个字符串代表同一个时刻,只是调整到一个时区而没有注意到offset-from-UTC
  • 通过始终在此类字符串中包含偏移量和区域信息来避免此类混淆。
  • 使用现代的 java.time 类来代替麻烦的遗留类。 (Instant,不是Timestamp

示例代码。

myPreparedStatement.setObject( 
    … , 
    myXMLGregorianCalendar  // If forced to work with a `javax.xml.datatype.XMLGregorianCalendar` object rather than a modern java.time class…
    .toGregorianCalendar()  // …convert to a `java.util.GregorianCalendar`, and then…
    .toZonedDateTime()      // …convert to modern `java.time.ZonedDateTime` class.
    .toInstant()            // Adjust to UTC by extracting an `Instant` object.
)

从 JDBC 4.2 及更高版本的数据库中检索。

Instant instant = myResultSet.getObject( … , Instant.class ) ;

java.time

正如correct accepted Answer by Robert Tupelo-Schneck 所说,似乎一切都很好,因为两个字符串都代表相同的时刻,但调整到具有与 UTC 不同偏移量的不同时区。问题是这两个字符串中的一个缺少其与 UTC 的偏移量的指示符。

这样的遗漏是一种不好的做法,因为它会造成这种混乱。除非绝对确定上下文明确了偏移量/区域,否则始终包含偏移量/UTC。

在 UTC 工作

在 UTC 工作可以避免这种混乱。通常最好在 UTC 中工作、存储和交换日期时间值。从 UTC 调整为仅用于向用户展示或业务逻辑需要时的时区。

此外,您正在使用非常麻烦的旧类,这些旧类现在已被 java.time 类所取代。将您的XMLGregorianCalendar 转换为java.time.ZonedDateTime

ZonedDateTime zdt = myXMLGregorianCalendar.toGregorianCalendar().toZonedDateTime() ;

通过提取Instant 对象来调整到UTC。

Instant instant = zdt.toInstant() ;

从 JDBC 4.2 及更高版本开始,您可以直接与数据库交换 java.time 对象。无需再次使用旧版 java.sql.Timestamp 类。

myPreparedStatement.setObject( … , instant ) ;

检索:

Instant instant = myResultSet.getObject( … , Instant.class ) ;

如果您想使用特定地区的人们使用的挂钟时间查看同一时刻,请从 UTC 调整到某个特定时区。

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

转换

如果您必须与需要java.sql.Timestamp 的旧代码交互,您可以使用java.time.Instant 来回转换。调用添加到旧类的新转换方法。

java.sql.Timestamp ts = java.sql.Timestamp.from( instant ) ;

往另一个方向发展。

Instant instant = ts.toInstant() ;

另请参阅my Answer 的类似问题。


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

    猜你喜欢
    • 2012-09-12
    • 2010-10-24
    • 2017-01-08
    • 2019-02-04
    • 1970-01-01
    • 2016-10-30
    • 1970-01-01
    • 2013-10-20
    • 1970-01-01
    相关资源
    最近更新 更多