java timestamp减一天,如何在Java中向时间戳添加/减去TimeZone偏移?

I am using JDK 8 and I played with ZonedDateTime and Timestamp a lot. But still I am not able to get a solution for the problem I am facing.

Lets say I get a formatted Timestamp in GMT (UTC) and my server is located somewhere. Lets say it's set to Asia/Calcutta TimeZone (whose ZoneOffset is +05:30).

How to basically add/subtract my time zone offset to the Timestamp?

Input : 2017-09-13 13:10:30.333

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

Explanation : In the input, the timezone offset which is 5hrs 30mts has been added to the input. Also, the input is of type Timestamp and so is the output.

I use ZoneID.systemDefault() function to retrieve the TimeZone known to JVM. In my case, the TimeZone turned out to be Asia/Calcutta.

解决方案

A java.sql.Timestamp doesn't have any timezone information. It just has one value*: the number of nanoseconds since unix epoch (1970-01-01T00:00Z or "January 1st 1970 at midnight in UTC"). You don't convert this value to a timezone because this number is not attached to any specific timezone.

When printing the Timestamp, it calls the toString() method, and this prints the corresponding date and time in the JVM default timezone. But the Timestamp itself doesn't have a timezone attached to it.

Take a look at this article for more info. It talks about java.util.Date, but the concept is the same: these objects don't carry any format or timezone information, so you can't convert them between timezones. What you can change is the representation of those values in different zones.

Example: if I take the 1505308230333000000 as the number of nanoseconds since epoch. This same number represents 13:10 in UTC, 10:10 in São Paulo, 14:10 in London, 22:10 in Tokyo, 18:40 in Calcutta, and so on.

The Timestamp class just keeps the big number value*. This same value corresponds to a different date/time in each timezone, but its value is always the same for everyone.

That's why converting the Timestamp between different zones makes no sense: your Timestamp object already represents both 13:10 in UTC and 18:40 in Calcutta (it contains the big number value that corresponds to these date/times in the respective timezones - changing this value will change the respective local date/times for all timezones).

What you can change is the String representation of this big number value (the corresponding local date/time in a specified timezone).

If you want to get a String with the corresponding date and time in another timezone, though, you can use the java.time classes.

First you need to convert the java.sql.Timestamp to a java.time.Instant, then convert it to a timezone, resulting in a java.time.ZonedDateTime. Finally, I format it to a String, using a 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

The output is:

2017-09-13 18:40:30.333

If you already know what timezone to use (in this case, Asia/Calcutta), don't use the default timezone (ZoneID.systemDefault()) - of course you can use it if you want, just keep in mind that it can be changed without notice, even at runtime, so it's better to always make it explicit which one you're using.

*Actually, the Timestamp keeps the big number value in two fields: one for the seconds and another for the nanoseconds value. But that's an implementation detail, that doesn't change the concept that this value is not attached to any timezone and so there's no point in converting the Timestamp between zones

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值