Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LocalDate properties depend on the machine's timezone setting [DATAMONGO-2627] #3482

Closed
spring-projects-issues opened this issue Sep 16, 2020 · 10 comments
Assignees
Labels
type: documentation A documentation update

Comments

@spring-projects-issues
Copy link

sothawo opened DATAMONGO-2627 and commented

When storing an entity with a LocalDate property in a Spring Data MongoDB Repository, on reading the entity the property's value depends on the system timezone setting.

LocalDates have no notion of a timezone, they are just a combination of year/month/day , so the value should not change.

I have linked a GitHub repo with a small demo application, in short:

Person margaret = new Person();
margaret.setId("1");
margaret.setName("Margaret Hamilton");
margaret.setBirthday(LocalDate.of(1936, 8, 17));
LOG.info("This is person 1: " + margaret + '\n');

LOG.info("Storing person 1 on a machine in Berlin\n");
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin"));
repository.save(margaret);

LOG.info("Retrieving person 1 on a machine in Los Angeles");
TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
Person person1 = repository.findById("1").orElseThrow();
LOG.info("This is person 1: " + person1);

This stores a person with her birthday in the database with one timezone setting and reads the entity with a different timezone setting. The birthday is then one day off.

2020-09-16 19:52:03.458  INFO 23828 --- : This is person 1: Person{id='1', name='Margaret Hamilton', birthday=1936-08-17}
2020-09-16 19:52:03.458  INFO 23828 --- : Storing person 1 on a machine in Berlin
2020-09-16 19:52:03.696  INFO 23828 --- : Retrieving person 1 on a machine in Los Angeles
2020-09-16 19:52:03.719  INFO 23828 --- : This is person 1: Person{id='1', name='Margaret Hamilton', birthday=1936-08-16}

Affects: 3.0.4 (Neumann SR4)

Reference URL: https://github.com/sothawo/spring-data-mongodb-localdate

Issue Links:

  • DATAMONGO-1026 Joda, JSR-310 and ThreeTenBp converters are timezone-sensitive
@spring-projects-issues
Copy link
Author

Mark Paluch commented

LocalDate and other Local… variants we always translated into java.util.Date so that MongoDB can persist the value.
Since Spring Data MongoDB 2.3, you can tweak that behavior to use the driver's built-in codecs for JSR-310 types by customizing MongoCustomConversions:

MongoCustomConversions.create(config -> config.useNativeDriverJavaTimeCodecs());

Jsr310Converters is provided by Spring Data Commons as general converter utility that is used by all Spring Data Modules with the goal to translate JSR-310 types into java.util.Date and back

@spring-projects-issues
Copy link
Author

sothawo commented

I know, that Mongo needs a java.util.Date, but I'd expect that I don't need to specify some custom behaviour to have a LocalDate persisted and retrieved. The documentation for mapping types specifies in the footnote for LocalDate:

??Uses UTC zone offset.??

But this is apparently not the case.

We implemented our own converters as a workaround which do use UTC:

@Configuration
class MongoConfig {
    @Bean
    fun mongoCustomConversions() = MongoCustomConversions(listOf(LocalDateReadingConverter(), LocalDateWritingConverter()))
}

@WritingConverter
class LocalDateWritingConverter : Converter<LocalDate, Date> {
    override fun convert(localDate: LocalDate): Date = Date(localDate.atStartOfDay(ZONEID_UTC).toEpochSecond() * 1000)
}

@ReadingConverter
class LocalDateReadingConverter : Converter<Date, LocalDate> {
    override fun convert(date: Date): LocalDate = date.toInstant().atZone(ZONEID_UTC).toLocalDate()
}

private val ZONEID_UTC = ZoneId.of("UTC")

@spring-projects-issues
Copy link
Author

sothawo commented

I just read the last comment (https://jira.spring.io/browse/DATAMONGO-1026?focusedCommentId=188441&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-188441) from the linked issue: If this is not to be changed, then at least the documentation should clearly state, that the timezone of the machine where the Spring application is running is used for writing and reading this value

@spring-projects-issues
Copy link
Author

Mark Paluch commented

The UTC reference is related to the native codec. See also https://github.com/mongodb/mongo-java-driver/blob/master/bson/src/main/org/bson/codecs/jsr310/LocalDateCodec.java#L57 for how the MongoDB driver translates the value.

I think it makes sense to make the local TimeZone setting for the Spring Converter more explicit to avoid further confusion

@spring-projects-issues
Copy link
Author

sothawo commented

Definitely the docs should be mor precise. And I agree with the local timezoe to be taken into consideration for all the temporal types except LocalDate where the Javadoc states:

??This class does not store or represent a time or time-zone. Instead, it is a description of the date, as used for birthdays.??

So I'd never expect this value to be dependent on a timezone, even it needs to be converted under the hood

@mp911de
Copy link
Member

mp911de commented Jan 20, 2021

The issue is somewhat related to relational databases. java.sql.Date is millisecond based while the majority of databases use zone-less dates. True timestamps (where java.sql.Date or Instant is the right type) are not so widely used. Since our JSR-310 support originates from JPA/JDBC, we always used a Date as interchange. Now one can argue whether LocalDate should be a string in MongoDB or a date (or even a subdocument). Both representations come with their own properties regarding sorting, querying and none of them is ideal.

That being said, we are required to inter-op with a driver-native type, hence timezones make an impact here.

Looking at the docs (https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongo.custom-converters.xml), we already have documented the impact of timezones. Can you @sothawo check whether that's sufficient?

@sothawo
Copy link

sothawo commented Jan 20, 2021

The documentation you link to is alright, but the table at https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mapping-conversion has a footnote for LocalDate that explicitly defines UTC. That's in contrast to the default converter implementation.

@christophstrobl
Copy link
Member

I agree that the conversion should use an UTC based approach that can be configured by using MongoDB's native JSR-310 support. Created #4460 to update the documentation in that regard.

@rishiraj88
Copy link

Thanks, @christophstrobl

@sothawo
Copy link

sothawo commented Jul 28, 2023

Almost forgot I had opend that issue 😁

mp911de pushed a commit that referenced this issue Aug 17, 2023
mp911de pushed a commit that referenced this issue Aug 17, 2023
@mp911de mp911de removed the type: bug A general bug label Aug 17, 2023
@mp911de mp911de added this to the 4.0.9 (2022.0.9) milestone Aug 17, 2023
@mp911de mp911de linked a pull request Aug 17, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: documentation A documentation update
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants