5

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.

Prasath Govind
  • 720
  • 2
  • 10
  • 30

1 Answers1

3

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

  • Hugo, I am using ZoneID.systemDefault() with its risk known. The ultimate output I need is again the time in UTC. I am using jackson annotations to deserialize my output to UTC. So, I need not bother about the TimeZone. To whichever TimeZone I convert my input, the ultimate output will again be converted back to UTC. ;) – Prasath Govind Sep 11 '17 at 19:15
  • If you need output in UTC, just use the `java.util.Timestamp` directly. If you could please edit the question and make it clearer about what's the expected output (because I understood that you wanted `2017-09-13 18:40:30.333`) –  Sep 11 '17 at 19:17
  • 2017-09-13 18:40:30.333 is the one I wanted. ;) Cheers. There is another layer in my project which I was talking about a little mind in my previous comment. Never Mind.. – Prasath Govind Sep 11 '17 at 19:19
  • @PrasathGovind Does it mean that it worked and solved your problem? If that's the case, you can either accept the answer and/or upvote it: https://stackoverflow.com/help/someone-answers (of course you're not obliged to do it, but if it solved your problem, it's considered a "good practice"). If it's not, just let me know so I can adjust the answer. Cheers! –  Sep 11 '17 at 19:42