Skip to content

Unable to find records by LocalDate on dates with clock changes occurring at midnight #1792

Open
@acmi

Description

@acmi

When using the java.time.LocalDate type as a parameter for querying (which is converted to java.sql.Timestamp during the execution), some databases return an empty result in certain cases. For instance, this occurs with dates where a clock change happens at midnight.

Code snippet to reproduce (Daylight Saving Time began in Egypt on April 26, 2024):

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.core.JdbcAggregateOperations;
import org.springframework.data.relational.core.mapping.Table;
import org.testcontainers.containers.JdbcDatabaseContainer;
import org.testcontainers.containers.MSSQLServerContainer;
import org.testcontainers.containers.MariaDBContainer;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.ext.ScriptUtils;
import org.testcontainers.jdbc.JdbcDatabaseDelegate;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import java.time.LocalDate;
import java.util.TimeZone;

import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.springframework.data.relational.core.query.Criteria.where;
import static org.springframework.data.relational.core.query.Query.query;

@SpringBootTest(properties = {
        "logging.level.org.springframework.jdbc.core=trace",
})
@Testcontainers
public class LocalDateTest {
    @Container
    @ServiceConnection
    static JdbcDatabaseContainer<?> db = new MariaDBContainer<>();        // AFFECTED
//    static JdbcDatabaseContainer<?> db = new MSSQLServerContainer<>();  // AFFECTED
//    static JdbcDatabaseContainer<?> db = new PostgreSQLContainer<>();   // NOT AFFECTED

    @Autowired
    JdbcAggregateOperations template;

    @BeforeAll
    static void setUp() throws Exception {
        TimeZone.setDefault(TimeZone.getTimeZone("Egypt"));

        ScriptUtils.executeDatabaseScript(
                new JdbcDatabaseDelegate(db, ""),
                null,
                "create table with_local_date (id int primary key, test_date date)"
        );
    }

    @ParameterizedTest
    @ValueSource(strings = {
            "2024-04-25", // OK   | 2024-04-24T23:59:59+02:00[Egypt] → 2024-04-25T00:00:00+02:00[Egypt]
            "2024-04-26", // FAIL | 2024-04-25T23:59:59+02:00[Egypt] → 2024-04-26T01:00:00+03:00[Egypt]
            "2024-04-27", // OK   | 2024-04-26T23:59:59+03:00[Egypt] → 2024-04-27T00:00:00+03:00[Egypt]
    })
    void saveAndFindByLocalDate(LocalDate testDate) {
        var entity = new WithLocalDate();
        entity.id = testDate.hashCode();
        entity.testDate = testDate;

        template.insert(entity);

        var loaded = template.findOne(query(where("testDate").is(testDate)), WithLocalDate.class);

        assertTrue(loaded.isPresent());
    }

    @Table
    static class WithLocalDate {
        @Id
        Integer id;
        LocalDate testDate;
    }
}

Metadata

Metadata

Assignees

Labels

theme: date-timeIssues related to handling of date, time and timezone informationtype: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions