【问题标题】:How to add/subtract TimeZone Offset to a Timestamp in Java?如何在 Java 中为时间戳添加/减去时区偏移量?
【发布时间】:2018-02-20 02:26:21
【问题描述】:

我使用的是 JDK 8,我经常使用 ZonedDateTimeTimestamp。但是我仍然无法解决我面临的问题。

假设我在格林威治标准时间 (UTC) 中获得了格式化的 Timestamp,并且我的服务器位于某处。假设它设置为 Asia/Calcutta TimeZone(其 ZoneOffset 为 +05:30)。

如何基本上将我的时区偏移量添加/减去时间戳?

Input : 2017-09-13 13:10:30.333
Output: 2017-09-13 18:40:30.333

解释:在输入中,时区偏移量为 5hrs 30mts 已添加到输入中。此外,输入是 Timestamp 类型,输出也是。

我使用ZoneID.systemDefault() 函数来检索 JVM 已知的 TimeZone。就我而言,TimeZone 原来是Asia/Calcutta

【问题讨论】:

  • 输入输出是日期类型还是字符串?
  • 输入不是字符串。这是时间戳。
  • 加/减是错误的思考方式。请参阅timezone tag wiki中的“时区!= 偏移量”
  • 我从来没有说过加/减时区。我说的是加/减时区偏移量。
  • 即便如此,添加或减去时间戳值并不是在另一个时区转换本地日期/时间的正确方法(正如我在下面的回答中解释的那样)

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


【解决方案1】:

java.sql.Timestamp 没有任何时区信息。它只有一个值*:自 unix 纪元以来的纳秒数(1970-01-01T00:00Z“UTC 时间 1970 年 1 月 1 日st 午夜”)。 您不会将此值转换为时区,因为此数字未附加到任何特定时区。

在打印Timestamp 时,它调用toString() 方法,这将打印JVM 默认时区中对应的日期和时间。但是Timestamp 本身并没有附加时区。

查看this article 了解更多信息。它谈到java.util.Date,但概念是一样的:这些对象不携带任何格式或时区信息,因此您无法在时区之间转换它们。您可以更改的是这些值在不同区域中的表示

示例:如果我将1505308230333000000 作为纪元以来的纳秒数。同样的数字代表 UTC 13:10、圣保罗 10:10、伦敦 14:10、东京 22:10、加尔各答 18:40,等等。

Timestamp 类只保留大数值*。这个相同的值对应于每个时区的不同日期/时间,但每个人的值始终相同

这就是为什么在不同区域之间转换 Timestamp 没有意义:您的 Timestamp 对象已经代表 UTC 13:10 和加尔各答 18:40(它包含大数值对应于各个时区中的这些日期/时间 - 更改此值将更改所有时区的相应本地日期/时间。

您可以更改的是String 表示这个大数值(指定时区中相应的本地日期/时间)。


但是,如果您想在另一个时区获得具有相应日期和时间的 String,则可以使用 java.time 类。

首先您需要将java.sql.Timestamp 转换为java.time.Instant,然后将其转换为时区,从而得到java.time.ZonedDateTime。最后,我将其格式化为String,使用java.time.format.DateTimeFormatter

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
// convert the timestamp to a zoneddatetime
ZonedDateTime z = timestamp.toInstant().atZone(ZoneId.of("Asia/Calcutta"));
// format it
System.out.println(z.format(fmt)); // 2017-09-13 18:40:30.333

输出是:

2017-09-13 18:40:30.333


如果您已经知道要使用哪个时区(在本例中为 Asia/Calcutta),请不要使用默认时区 (ZoneID.systemDefault()) - 当然您可以根据需要使用它,只要记住它can be changed without notice, even at runtime,因此最好始终明确说明您使用的是哪一个。


*实际上,Timestamp 将大数值保存在两个字段中:一个用于秒,另一个用于纳秒值。但这是一个实现细节,不会改变这个值不附加到任何时区的概念,因此在区域之间转换 Timestamp 没有意义

【讨论】:

  • Hugo,我正在使用 ZoneID.systemDefault() ,它的风险是已知的。我需要的最终输出再次是 UTC 时间。我正在使用杰克逊注释将我的输出反序列化为 UTC。所以,我不需要担心时区。无论我将输入转换为哪个 TimeZone,最终输出都将再次转换回 UTC。 ;)
  • 如果您需要UTC格式的输出,直接使用java.util.Timestamp即可。如果可以请编辑问题并更清楚地说明预期的输出是什么(因为我知道你想要2017-09-13 18:40:30.333
  • 2017-09-13 18:40:30.333 是我想要的。 ;) 干杯。我的项目中还有另一层,我在之前的评论中谈到了一个小头脑。没关系..
  • @PrasathGovind 这是否意味着它有效并解决了您的问题?如果是这种情况,您可以接受答案和/或投票:stackoverflow.com/help/someone-answers(当然您没有义务这样做,但如果它解决了您的问题,这被认为是一种“好习惯”)。如果不是,请告诉我,以便我调整答案。干杯!
猜你喜欢
  • 2014-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-06
  • 1970-01-01
  • 2016-04-29
  • 1970-01-01
相关资源
最近更新 更多