Timestamp
& Instant
always in UTC
The problem I'm having is that .toInstant() add local time offset to the Timestamp object
No it does not.
Neither can have any other zone assigned.
Do not bunch up your code into a single line. Break out each step into separate lines so you can debug their values.
java.sql.Timestamp ts = rs.getTimestamp("Start") ; // Actually in UTC, but it's `toString` method applies JVM’s current default time zone while generating string.
Instant instant = ts.toInstant() ; // Same moment, also in UTC.
ZoneId z = ZoneId.of( "America/Montreal" ) ; // Or call your global var: `Globals.LOCALZONEID`.
ZonedDateTime zdt = instant.atZone( z ); // Same moment, same point on timeline, but with wall-clock time seen in a particular zone.
After that, you may see the issue (or non-issue). If not, edit your question to show the debug values of each of these variables.
Do not trust Timestamp::toString
Important: The java.sql.Timestamp::toString
method lies. That method applies your JVM’s current default time zone while generating the string. The actual value is always in UTC. One of many reasons to avoid these troublesome legacy classes. Run the following code example on your own machine to see the influence of your default time zone on the textual representation of the Timestamp
.
Let’s run a simulation of that code running live in IdeOne.com. The JVM at IdeOne.com defaults to UTC/GMT, so we override default by specifying the default as Pacific/Auckland
arbitrarily.
Instant now = Instant.now() ; // Simulating fetching a `Timestamp` from database by using current moment in UTC.
TimeZone.setDefault( TimeZone.getTimeZone( "Pacific/Auckland" ) ) ;
ZoneId zoneIdDefault = ZoneId.systemDefault() ;
ZoneOffset zoneOffset = zoneIdDefault.getRules().getOffset( now ) ;
java.sql.Timestamp ts = java.sql.Timestamp.from( now ) ; // Actually in UTC, but it's `toString` method applies JVM’s current default time zone while generating string.
Instant instant = ts.toInstant() ; // Same moment, also in UTC.
ZoneId z = ZoneId.of( "America/Montreal" ) ; // Or call your global var: `Globals.LOCALZONEID`.
ZonedDateTime zdt = instant.atZone( z ); // Same moment, same point on timeline, but with wall-clock time seen in a particular zone.
Current default time zone: Pacific/Auckland
Current default offset-from-UTC: Pacific/Auckland | total seconds: 43200
now.toString(): 2017-06-09T04:41:10.750Z
ts.toString(): 2017-06-09 16:41:10.75
instant.toString(): 2017-06-09T04:41:10.750Z
z.toString(): America/Montreal
zdt.toString(): 2017-06-09T00:41:10.750-04:00[America/Montreal]
Avoid legacy date-time classes
The old date-time classes found outside the java.time package are troublesome, confusing, badly designed, and flawed. Avoid them whenever possible. This includes java.sql.Timestamp
.
Your JDBC 4.2 compliant driver can directly address java.time types by calling PreparedStatement::setObject
and ResultSet::getObject
.
myPreparedStatement.setObject( … , instant ) ;
… and …
Instant instant = myResultSet.getObject( … , Instant.class ) ;
If using a JDBC driver not yet updated to JDBC 4.2 and java.time, convert briefly to java.sql.Timestamp
using new methods added to the old class: from ( Instant )
, toInstant(), and such. But beyond exchanging data with the database, do all your real work (business logic) in java.time objects.
myPreparedStatement.setTimestamp( … , java.sql.Timestamp.from( instant ) ) ;
… and …
Instant instant = myResultSet.getTimestamp( … ).toInstant() ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.*
classes.
Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.