Cannot reproduce your test failure
Your code:
LocalDate.parse( "1900-01-01" )
…works as documented, producing a value of January 1, 1900.
LocalDate ld = LocalDate.parse("1900-01-01") ;
System.out.println( ld ) ;
See that code run live at IdeOne.com.
1900-01-01
➥ There must be more to your code than you showed us.
Example app using H2
You said:
I guess the change is made during the saving-action at the H2 database.
Let's try it. Here is an entire example app using H2 version 1.4.200. Ran from IntelliJ 2020.1 on macOS Mojave with Java 14.
We have a table event_
with an id_
column of type UUID, and a column when_
of type LocalDate
. We write a single value for a value of 1900-01-01.
As you can see if you run this code, we get back a LocalDate
object for which the method toString
returns 1900-01-01
.
package work.basil.example;
import org.h2.jdbcx.JdbcDataSource;
import java.sql.*;
import java.time.LocalDate;
import java.util.Objects;
import java.util.UUID;
public class H2Example
{
public static void main ( String[] args )
{
H2Example app = new H2Example();
app.doIt();
}
private void doIt ( )
{
JdbcDataSource dataSource = Objects.requireNonNull( new JdbcDataSource() ); // Implementation of `DataSource` bundled with H2.
dataSource.setURL( "jdbc:h2:mem:localdate_example_db;DB_CLOSE_DELAY=-1" ); // Set `DB_CLOSE_DELAY` to `-1` to keep in-memory database in existence after connection closes.
dataSource.setUser( "scott" );
dataSource.setPassword( "tiger" );
String sql = null;
try (
Connection conn = dataSource.getConnection() ;
)
{
String tableName = "event_";
sql = "CREATE TABLE " + tableName + " (\n" +
" id_ UUID DEFAULT random_uuid() PRIMARY KEY ,\n" +
" when_ DATE NOT NULL\n" +
");";
try ( Statement stmt = conn.createStatement() ; )
{
stmt.execute( sql );
}
// Insert row.
sql = "INSERT INTO event_ ( when_ ) " + "VALUES ( ? ) ;";
try (
PreparedStatement preparedStatement = conn.prepareStatement( sql ) ;
)
{
LocalDate localDate = LocalDate.parse( "1900-01-01" );
preparedStatement.setObject( 1 , localDate ); // Yesterday.
preparedStatement.executeUpdate();
}
// Query all.
sql = "SELECT * FROM event_ ;";
try (
Statement stmt = conn.createStatement() ;
ResultSet rs = stmt.executeQuery( sql ) ;
)
{
while ( rs.next() )
{
//Retrieve by column name
UUID id = rs.getObject( "id_" , UUID.class ); // Pass the class to be type-safe, rather than casting returned value.
LocalDate localDate = rs.getObject( "when_" , LocalDate.class ); // Ditto, pass class for type-safety.
//Display values
System.out.println( "id_: " + id + " | when_: " + localDate );
}
}
}
catch ( SQLException e )
{
e.printStackTrace();
}
}
}
When that example app runs:
id_: 949830e0-77c8-49a3-8804-0972ff70ca2c | when_: 1900-01-01
Do not rely on default time zone
Your code:
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
…is quite risky at deployment. Setting the time zone immediately affects all code in all threads of all apps running within that JVM. You could be pulling out the carpet from beneath the feet of other apps, or ever other parts of your own app.
➥ Better to never rely on the JVM’s current default time zone.
To capture the current moment as seen in UTC, simply call Instant.now
. An Instant
represents a moment in UTC with a resolution of nanoseconds. This class replaces java.util.Date
.
Instant instant = Instant.now() ; // Capture the current moment in UTC.
For database work, use the more flexible class OffsetDateTime
. The JDBC 4.2 specification inexplicably requires support for OffsetDateTime
but not the more commonly used Instant
and ZonedDateTime
.
OffsetDateTime odt = OffsetDateTime.now( ZoneOffset.UTC ) ; // Capture the current moment in UTC.
Write that to the database.
myPreparedStatement.setObject( … , odt ) ;
Retrieval.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
When recording a moment (a specific point on the timeline) such as that represented by OffsetDateTime
, your column in the database must be of a data type akin to the SQL standard type TIMESTAMP WITH TIME ZONE
(not WITHOUT
). In H2, the data type to use has that very name, TIMESTAMP WITH TIME ZONE
.
java.time.LocalDate
can be used withDATE
data type from H2 safely with any time zone, they both don't depend on it. But if libraries used by your application use legacyjava.sql.Date
internally, they may be affected by different bugs of pre-Java 8 datetime classes; many persistence libraries do weird conversions to / from legacy classes even when database has native support for modern JSR-310 classes. These bugs, however, usually don't affect the UTC time zone. You need to add more details to your question. – Lourdeslourie