Timestamp#toInstant
is indeed in UTC
convert the stored Timestamp to an Instant
Calling java.sql.Timestamp#toInstant
will indeed give you a date and time as seen with an offset of zero hours-minutes-seconds from UTC. Both classes internally keep a count since the epoch reference of first moment of 1970 in UTC, 1970-01-01T00:00Z.
Instant instant = myJavaSqlTimestamp.toInstant() ;
Avoid legacy date-time classes
The Timestamp
class is one of the terribly flawed legacy date-time classes. These were years ago supplanted by the modern java.time classes defined in JSR 310.
Never use Timestamp
, Calendar
, Date
, SimpleDateFormat
, etc. Use only java.time classes with JDBC 4.2+.
With zone/offset
If the column in your Microsoft SQL Server database is of the type datetimeoffset
, then retrieve the matching type in JDBC: OffsetDateTime
.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Writing:
String input = "2023-08-08 07:52:09.877Z" ; // `Z` on the end indicates an offset of zero hours-minutes-seconds from UTC. Pronounced "Zulu".
Instant instant = Instant.parse( input ) ; // Represents a moment as seen with an offset of zero from UTC.
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ; // Represents a moment as seen with a particular offset from UTC. In this case the offset is zero.
myPreparedStatement.setObject( … , odt ) ;
Without zone/offset
I suspect your problem is due to an incorrect data type on your column. You neglected to indicate the data type, so I can only guess. But I guess the type is the legacy Microsoft SQL Server type datetime
or its modern replacement datetime2
.
Both of those datetime
/datetime2
types represent a date with time-of-day but lack the context of a time zone or an offset-from-UTC. Without that context, such a value does not represent a moment, is not a specific point on the timeline. So comparing to a value in UTC is nonsensical.
The matching JDBC type for those datetime
/datetime2
types is LocalDateTime
— a class representing a date with time-of-day but without zone/offset.
LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;
Writing:
myPreparedStatement.setObject( … , ldt ) ;
Do not use those datetime
/datetime2
types nor LocalDateTime
if you are tracking a moment, a specific point on the timeline. That would be like storing an amount of money without noting the currency.
If you did indeed use those datetime
/datetime2
types in an attempt to store a moment, then you now have a mess on your hands. You will need to use a datetimeoffset
instead. To migrate data, you can assign an offset if you know for certain the intended offset of the stored values, but you will of course ultimately be guessing since you cannot know the context of every row’s insertion/update.