Search code examples
javajdbcjava-8h2localdate

Year changing from negative -509 to a positive 510 in JDBC with H2 Database


-509 vs 510

I am seeing some kind of altered or erroneous data, with the use of JDBC. So I observe using H2 Database version 1.4.196 on Java 8 Update 151.

Here is a complete example.

Note how we retrieve the date value three times, first as a LocalDate object, secondly as text, and thirdly as an int year number extracted from a cast LocalDate object. In the text version we can see that the year is indeed negative. Mysteriously the LocalDate has a different year number and it is positive rather than negative. Seems like a bug.

private void doIt ( )
{
    System.out.println( "BASIL - Running doIt." );
    try
    {
        Class.forName( "org.h2.Driver" );
    } catch ( ClassNotFoundException e )
    {
        e.printStackTrace( );
    }

    try (
            Connection conn = DriverManager.getConnection( "jdbc:h2:mem:" ) ;  // Unnamed throw-away in-memory database.
    )
    {
        conn.setAutoCommit( true );
        String sqlCreate = "CREATE TABLE history  ( id IDENTITY , when  DATE ); ";
        String sqlInsert = "INSERT INTO history ( when ) VALUES ( ? ) ; ";
        String sqlQueryAll = "SELECT * FROM history  ; ";

        PreparedStatement psCreate = conn.prepareStatement( sqlCreate );

        psCreate.executeUpdate( );

        PreparedStatement psInsert = conn.prepareStatement( sqlInsert );

        psInsert.setObject( 1 , LocalDate.of( 2017 , 1 , 23 ) );
        psInsert.executeUpdate( );

        psInsert.setObject( 1 , LocalDate.of( -509 , 1 , 1 ) );
        psInsert.executeUpdate( );

        PreparedStatement psQueryAll = conn.prepareStatement( sqlQueryAll );
        ResultSet rs = psQueryAll.executeQuery( );
        while ( rs.next( ) )
        {
            long l = rs.getLong( 1 );  // Identity column.
            // Retrieve the same date value in three different ways.
            LocalDate ld = rs.getObject( 2 , LocalDate.class );  // Extract a `LocalDate`, and implicitly call its `toString` method that uses standard ISO 8601 formatting.
            String s = rs.getString( 2 );  // Extract the date value as text from the database using the database-engine’s own formatting.
            int y = ( ( LocalDate ) rs.getObject( 2 , LocalDate.class ) ).getYear( );  // Extract the year number as an integer from a `LocalDate` object.
            String output = "ROW: " + l+ " | " + ld + " | when as String: " + s+ " | year: " + y ;
            System.out.println( output );
        }

        conn.close( );
    } catch ( SQLException e )
    {
        e.printStackTrace( );
    }
}

When run.

ROW: 1 | 2017-01-23 | when as String: 2017-01-23 | year: 2017

ROW: 2 | 0510-01-01 | when as String: -509-01-01 | year: 510

So there seems to be something going on with JDBC being involved. Note how the year is presented as a positive 510 rather than a negative 509. I do not understand this behavior.

I can deduce that it is an issue within JDBC rather than within LocalDate. See this example code run live in IdeOne.com showing that a LocalDate object does indeed carry and report a negative year.

LocalDate ld = LocalDate.of( -509 , 1 , 1 ) ;
System.out.println( "ld.toString(): " + ld ) ;
System.out.println( "ld.getYear(): " + ld.getYear() ) ;

Notice how we do not get converted from -509 to 510 when dealing with a LocalDate only, with no JDBC.

ld: -0509-01-01

ld.getYear(): -509

I opened a Issue ticket on the H2 project.


Solution

  • Bug, fixed

    This problem was due to a bug in H2.

    Now fixed, as of 2018-01.