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