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

时间:2019-05-14 16:49:52

标签: java datetime timezone timezone-offset java-date

我们在sqlserver db表中将日期存储为varchar。 当在Java代码中将其作为字符串读取然后解析为Date时,它将被读取为UTC(Java代码位于UT中的服务器中)。将日期重新转换为ET时要晚4小时。如何处理将日期存储在ET的此db列中,以便在Java代码中将其读取为ET。

我们正在研究偏移量,但不完全了解该怎么做。

表03/29/2019 23:23:03中的Varchar日期//我们希望此日期位于ET

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

//问题是执行此代码时,服务器处于UTC状态。因此beginDate //读取为UTC时间03/29/2019 23:23:03而不是美国东部时间03/29/2019 23:23:03

预计将于03/29/2019 23:23:03美国东部时间 实际的03/29/2019 23:23:03 UTC

3 个答案:

答案 0 :(得分:4)

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

第二,您需要意识到存储这样的值会引起歧义-如果存储的值是(例如)11/03/2019 01:30,则本地时间会发生两次-一次发生在夏令时之前,一次发生然后。如果您始终存储过去的时间,则应至少考虑存储UTC-尽管that's not always the right answer, particularly not if you're storing future date/time values

对于解析部分,您只需要在Calendar使用的SimpleDateFormat中设置时区。例如:

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。与java.util.Date等API相比,它是一个更好的API。

答案 1 :(得分:2)

java.time

Answer by Jon Skeet是正确的。正如他提到的那样,您应该使用JSR 310定义的 java.time 类。几年前,这些现代类取代了可怕的日期时间类,例如DateSimpleDateFormat 。这是该方向的一些示例代码。

  

表03/29/2019 23:23:03中的Varchar日期//我们希望此日期位于ET

将字符串解析为LocalDateTime,因为我们的输入缺少指示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,其中偏移量设置为零小时-分钟-秒(换句话说,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列。

答案 2 :(得分:2)

  1. 使用数据库引擎(DBMS)的日期时间数据类型,而不是varchar作为日期和/或时间。对于许多DBMS,SQL标准中定义的timestamp with timezone类型是要使用的正确类型。我对SQL Server的了解不够深,无法确切说明您应该在其中使用什么。确保将日期和时间存储在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)的答案向您展示了如何,我无需重复。