spring-projects / spring-projects/spring-data-relational

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

Open
#1,792 2 comments 0 reactions 1 assignee View on GitHub

@schauder is already working on this.

Since Sep 12, 2024.

theme: date-time type: enhancement
Dominant language
Java
Stars
827
Forks
394
PR merge metrics
No merged PRs in 30d

Description

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;
    }
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.