当 java 代码以 UTC 执行时,如何在 ET 中存储带有偏移量的日期

How to store a date in ET with offset when the java code executes in UTC

我们将日期存储在 sqlserver 数据库中 table 作为 varchar。 当在 java 代码中将其作为字符串读取然后解析为日期时,它会作为 UTC 读取(java 代码位于 UT 中的服务器中)。在将日期重新转换为 ET 时,它落后了 4 小时。我如何处理将日期存储在该数据库列中的 ET 中,以便在 java 代码中将其读取为 ET。

我们正在研究偏移量,但不了解具体要做什么。

Varchar date in table 03/29/2019 23:23:03 //我们希望这个日期在 ET

SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
Date beginDate = sdf.parse("03/29/2019 23:23:03");

//问题是当这段代码执行时,服务器是UTC。所以 beginDate // 读作 03/29/2019 23:23:03 UTC 而不是 03/29/2019 23:23:03 ET

预计 2019 年 3 月 29 日 23:23:03 东部时间 实际 03/29/2019 23:23:03 UTC

首先,您需要注意 Date 对象根本没有时区 。时间只是一瞬间。因此,即使您正确解析了该值,它也会代表正确的时间,但您稍后可能需要将其转换回东部时间。

其次,您需要注意像这样存储值会引入歧义 - 如果您存储(比如)11/03/2019 01:30 的值,则当地时间出现两次 - 一次在日光之前保存过渡和之后一次。如果您总是存储过去的时间,您至少应该 考虑 存储 UTC - 虽然 that's not always the right answer, particularly not if you're storing future date/time values.

解析部分只需要在SimpleDateFormat使用的Calendar中设置时区即可。例如:

SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss", Locale.US);
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York");
Date beginDate = sdf.parse("03/29/2019 23:23:03");

最后,我强烈 建议您开始迁移您的代码以尽可能使用 java.time。它 API 比 java.util.Date 等 API.

好得多

java.time

是正确的。正如他提到的,您应该使用 JSR 310 定义的 java.time 类。这些现代 类 年前取代了可怕的 date-time 类 例如 DateSimpleDateFormat。这是该方向的一些示例代码。

Varchar date in table 03/29/2019 23:23:03 //we want this date to be in ET

将字符串解析为 LocalDateTime,因为我们的输入缺少 offset-from-UTC 或时区的指示符。

定义格式模式以匹配输入。

DateTimeFormatter f = DateTimeFormatter.ofPattern( "MM/dd/uuuu HH:mm:ss" ) ;

解析。

LocalDateTime ldt = LocalDateTime.parse( input , f ) ;

如果您绝对确定此日期和时间适用于特定时区,请应用 ZoneId 以获得 ZonedDateTime

ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ;

通过提取 Instant.

调整为 UTC
Instant instant = zdt.toInstant() ;

您的 JDBC 驱动程序可能不接受 Instant。因此,转换为 OffsetDateTime,其中偏移量设置为零 hours-minutes-seconds(换句话说,UTC 本身)。

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;

写入数据库中 TIMESTAMP WITH TIME ZONE 类型的列。从 JDBC 4.2 及更高版本开始,我们可以直接与数据库交换 java.time 对象。

myPreparedStatement.setObject( … , odt ) ;

并检索。

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

调整到您想要的时区。

ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;

现在您已准备好执行一些 database-refactoring 所需的部分,用适当的 TIMESTAMP WITH TIME ZONE 列替换该 varchar 列。

  1. 使用数据库引擎 (DBMS) 的日期时间数据类型,而不是 varchar 日期 and/or 时间。对于许多 DBMS,SQL 标准中定义的 timestamp with timezone 类型是要使用的正确类型。我不太了解 SQL 服务器,无法准确判断您应该在那里使用什么。确保以 UTC 格式存储日期和时间。
  2. 如果您无法绕过存储为 varchar 的要求,请以 UTC 中的 ISO 8601 格式存储。例如 2019-03-30T03:23:03Z.
  3. 如果您也无法绕过按东部时间存储的要求,请确保您清楚是指北美东部时间还是澳大利亚东部时间。使用 UTC 偏移存储日期和时间,例如 2019-03-29T23:23:03-04:00.
  4. 如果您也无法绕过以 03/29/2019 23:23:03 格式(无偏移)存储的要求,请注意在秋季夏令时 (DST) 结束且时钟向后移动时,您是存储模糊时间。

在任何情况下都更喜欢 java.time,现代 Java 日期和时间 API。 Basil Bourque 的回答告诉你如何,我不需要重复。