OffsetDateTime
when I use Instant.now() to get the current UTC time,
Don’t use Instant for SQL database work.
In JDBC 4.2+, the specification maps OffsetDateTime class to columns of a type akin to the SQL standard type TIMESTAMP WITH TIME ZONE.
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ;
Neither Instant nor ZonedDateTime are mapped in JDBC. The SQL standard defines no such types equivalent to those classes.
By the way, for columns of a type akin to the SQL standard type TIMESTAMP WITHOUT TIME ZONE, use the LocalDateTime class.
Avoid legacy date-time classes
a conversion of Timestamp.from(instant)
Never use the terrible legacy date-time classes such as Timestamp. Use only their replacement: the modern java.time classes defined in JSR 310.
Write to the database:
myPreparedStatement.setObject … , odt ) ;
Retrieve:
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Do not depend on default zone
You commented:
The issue was my JVM was defaulting to local time
You should write your Java code in such a way as to not care about the JVM’s current default time zone.
The code shown above is unaffected by
the JVM’s current default time zone.
Example code
Here is a complete example.
package work.basil.example.db;
import javax.sql.DataSource;
import java.sql.*;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.UUID;
public class DemoH2InMem
{
public static void main ( String[] args )
{
DemoH2InMem app = new DemoH2InMem();
app.demo();
}
private void demo ( )
{
DataSource dataSource = this.fetchDataSource();
this.createTable( dataSource );
this.insertDummyData( dataSource );
this.dump( dataSource );
// Scanner scanner = new Scanner( System.in );
// System.out.print( "Type anything to end program: " );
// String anything = scanner.nextLine();
System.out.println( "Demo done at " + Instant.now() );
}
private DataSource fetchDataSource ( )
{
org.h2.jdbcx.JdbcDataSource dataSource = new org.h2.jdbcx.JdbcDataSource();
dataSource.setURL( "jdbc:h2:mem:demo_db;DB_CLOSE_DELAY=-1" );
return dataSource;
}
private void createTable ( final DataSource dataSource )
{
String sql =
"""
CREATE TABLE bogus_ (
id_ UUID PRIMARY KEY ,
when_ TIMESTAMP WITH TIME ZONE
)
;
""";
try (
Connection conn = dataSource.getConnection() ;
Statement stmt = conn.createStatement() ;
)
{
stmt.execute( sql );
}
catch ( SQLException e ) { throw new RuntimeException( e ); }
}
private void insertDummyData ( final DataSource dataSource )
{
String sql =
"""
INSERT INTO bogus_ ( id_ , when_ )
VALUES ( ? , ? )
;
""";
try (
Connection conn = dataSource.getConnection() ;
PreparedStatement pstmt = conn.prepareStatement( sql ) ;
)
{
pstmt.setObject( 1 , UUID.fromString( "97a9e379-4d8f-4d06-8bea-43560a72120b" ) );
pstmt.setObject( 2 , OffsetDateTime.now( ZoneOffset.UTC ) );
pstmt.executeUpdate();
pstmt.setObject( 1 , UUID.fromString( "052ae129-d0ca-4fdf-9a06-c87d20a2d3f2" ) );
pstmt.setObject( 2 , OffsetDateTime.now( ZoneOffset.UTC ) );
pstmt.executeUpdate();
}
catch ( SQLException e ) { throw new RuntimeException( e ); }
}
private void dump ( final DataSource dataSource )
{
String sql =
"""
SELECT * FROM bogus_
;
""";
try (
Connection conn = dataSource.getConnection() ;
Statement stmt = conn.createStatement() ;
ResultSet resultSet = stmt.executeQuery( sql ) ;
)
{
System.out.println( "-------------| table start |---------------" );
while ( resultSet.next() )
{
UUID uuid = resultSet.getObject( "id_" , UUID.class );
OffsetDateTime when = resultSet.getObject( "when_" , OffsetDateTime.class );
System.out.println( uuid + " | " + when );
}
System.out.println( "-------------| table end |---------------" );
}
catch ( SQLException e ) { throw new RuntimeException( e ); }
}
}
When run:
-------------| table start |---------------
97a9e379-4d8f-4d06-8bea-43560a72120b | 2023-02-10T20:32:57.074979Z
052ae129-d0ca-4fdf-9a06-c87d20a2d3f2 | 2023-02-10T20:32:57.080153Z
-------------| table end |---------------
Demo done at 2023-02-10T20:32:57.092230Z
ps.setObject(1, OffsetDateTime.now(ZoneOffset.UTC))(orLocalDateTime.now(ZoneOffset.UTC)if you’re so unfortunate that the database column doesn’t carry the information that it’s in UTC).Instant.nowwill be unaffected byuser.timezoneTimestampclass at all. In the old days it was used with SQL databases, but it was always poorly deigned, a true hack on top of the already badDateclass. Since JDBC 4.2 instead useOffsetDateTime,InstantorLocalDateTime. Stick to the modern API.Timestampis but a troublesome detour.