我正在使用JDK 8,而且我经常使用ZonedDateTime
和Timestamp
。但是我仍然无法解决我面临的问题。
假设我在GMT(UTC)中获得格式化Timestamp
,而我的服务器位于某处。让我们说它设置为Asia/Calcutta
TimeZone(其ZoneOffset为+05:30
)。
如何基本上将时区偏移量加/减到时间戳?
Input : 2017-09-13 13:10:30.333
Output: 2017-09-13 18:40:30.333
说明:在输入中,时区偏移量为5小时30毫米,已添加到输入中。此外,输入的类型为Timestamp,输出也是如此。
我使用ZoneID.systemDefault()
函数来检索JVM已知的TimeZone。就我而言,TimeZone原来是Asia/Calcutta
。
答案 0 :(得分:2)
java.sql.Timestamp
没有任何时区信息。它只有一个值*:自UTC时间(1970-01-01T00:00Z
或“1月1日 st 1970年午夜的UTC”)以来的纳秒数。 您不会将此值转换为时区,因为此数字未附加到任何特定时区。
打印Timestamp
时,它会调用toString()
方法,并在JVM默认时区中打印相应的日期和时间。但是Timestamp
本身并没有附加时区。
请查看this article了解详情。它讨论java.util.Date
,但概念是相同的:这些对象不携带任何格式或时区信息,因此您无法在时区之间进行转换。您可以更改的是不同区域中这些值的表示。
示例:如果我将1505308230333000000
作为自纪元以来的纳秒数。这个数字代表UTC的13:10,圣保罗的10:10,伦敦的14:10,东京的22:10,加尔各答的18:40,等等。
Timestamp
类只保留大数值*。此相同值对应于每个时区中的不同日期/时间,但其值对于每个人始终相同。
这就是为什么在不同区域之间转换Timestamp
没有任何意义:您的Timestamp
对象已经代表 13:10(UTC)和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
没有意义