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

Issue serializing and deserializing LocalDateTime.MAX and LocalDateTime.MIN #124

Open
bachilast opened this issue Aug 8, 2019 · 13 comments
Labels
Milestone

Comments

@bachilast
Copy link

When I use in a JUnit Test LocalDateTime.MAX and LocalDateTime.MIN I recieve an exception. See exception below.

I use

  • SpringBoot version 2.1.3
  • Jackson 2.9.9
  • Junit Jupiter 5.3.1

2019-08-08 13:30:00.493 ERROR 47028 --- [o-auto-1-exec-9] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type java.time.LocalDateTime from String "01.01.+1000000000 00:00:00": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '01.01.+1000000000 00:00:00' could not be parsed: Invalid value for Year (valid values -999999999 - 999999999): 1000000000; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type java.time.LocalDateTime from String "01.01.+1000000000 00:00:00": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '01.01.+1000000000 00:00:00' could not be parsed: Invalid value for Year (valid values -999999999 - 999999999): 1000000000
at [Source: (PushbackInputStream); line: 1, column: 144] (through reference chain: with root cause

java.time.DateTimeException: Invalid value for Year (valid values -999999999 - 999999999): 1000000000
at java.time.temporal.ValueRange.checkValidIntValue(ValueRange.java:330) ~[na:1.8.0_141]
at java.time.temporal.ChronoField.checkValidIntValue(ChronoField.java:722) ~[na:1.8.0_141]
at java.time.chrono.IsoChronology.resolveYMD(IsoChronology.java:544) ~[na:1.8.0_141]
at java.time.chrono.IsoChronology.resolveYMD(IsoChronology.java:123) ~[na:1.8.0_141]
at java.time.chrono.AbstractChronology.resolveDate(AbstractChronology.java:472) ~[na:1.8.0_141]
at java.time.chrono.IsoChronology.resolveDate(IsoChronology.java:492) ~[na:1.8.0_141]
at java.time.chrono.IsoChronology.resolveDate(IsoChronology.java:123) ~[na:1.8.0_141]
at java.time.format.Parsed.resolveDateFields(Parsed.java:351) ~[na:1.8.0_141]
at java.time.format.Parsed.resolveFields(Parsed.java:257) ~[na:1.8.0_141]
at java.time.format.Parsed.resolve(Parsed.java:244) ~[na:1.8.0_141]
at java.time.format.DateTimeParseContext.toResolved(DateTimeParseContext.java:331) ~[na:1.8.0_141]
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1955) ~[na:1.8.0_141]
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851) ~[na:1.8.0_141]
at java.time.LocalDateTime.parse(LocalDateTime.java:492) ~[na:1.8.0_141]
at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer.deserialize(LocalDateTimeDeserializer.java:82) ~[jackson-datatype-jsr310-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer.deserialize(LocalDateTimeDeserializer.java:39) ~[jackson-datatype-jsr310-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeSetAndReturn(MethodProperty.java:158) ~[jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.deserializeFromObject(BuilderBasedDeserializer.java:314) ~[jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.deserialize(BuilderBasedDeserializer.java:195) ~[jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeSetAndReturn(MethodProperty.java:158) ~[jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.deserializeFromObject(BuilderBasedDeserializer.java:314) ~[jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.deserialize(BuilderBasedDeserializer.java:195) ~[jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013) ~[jackson-databind-2.9.9.jar:2.9.9]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3084) ~[jackson-databind-2.9.9.jar:2.9.9]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:239) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:227) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:204) ~[spring-webmvc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:157) ~[spring-webmvc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:130) ~[spring-webmvc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:126) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:166) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) ~[spring-webmvc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800) ~[spring-webmvc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038) ~[spring-webmvc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) ~[spring-webmvc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) ~[spring-webmvc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908) ~[spring-webmvc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) ~[spring-webmvc-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.16.jar:9.0.16]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:96) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) ~[spring-security-web-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) [tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) [tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834) [tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415) [tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.16.jar:9.0.16]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_141]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_141]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.16.jar:9.0.16]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_141]

@cowtowncoder
Copy link
Member

I would need to have a full reproduction: stack trace itself is unfortunately not enough to tell what is going on (for example: is serialization producing incorrect value -- it seems so, as value appears slightly off rnage).

@kupci
Copy link
Member

kupci commented Mar 18, 2020

I think this is a valid error message, the MAX year is 999999999 per Oracle documentation:

The maximum supported LocalDateTime, '+999999999-12-31T23:59:59.999999999'.

Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '01.01.+1000000000 00:00:00' could not be parsed: Invalid value for Year (valid values -999999999 - 999999999): 1000000000;

If the year is 999999999 the deserialization is working, as does the year -99999999. I'll add some edge case tests. Perhaps, given #166, it may be that, somewhere along the line (within Spring Boot?) there is some date conversion going on that is tipping the date over to the year 1000000000. This is where the full repro mentioned by CowtownCoder would be helpful.

@stdname
Copy link

stdname commented Apr 27, 2020

It is fairly easy to reproduce without Spring (The language is Kotlin, but it should be easy to map to Java in 1:1 fashion):

data class DateHolder(
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
  val date: LocalDateTime
)

fun main() {
  val mapper = ObjectMapper().registerKotlinModule().registerModule(JavaTimeModule())
  val data = DateHolder(LocalDateTime.MIN)
  val serialized = mapper.writeValueAsString(data)
  val wontGetToThisPoint = mapper.readValue(serialized, DateHolder::class.java)
}

Running main() results in:

Exception in thread "main" com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDateTime` from String "+1000000000-01-01 00:00": Text '+1000000000-01-01 00:00' could not be parsed: Invalid value for Year (valid values -999999999 - 999999999): 1000000000
 at [Source: (String)"{"date":"+1000000000-01-01 00:00"}"; line: 1, column: 9] (through reference chain: xxx.DateHolder["date"])
	at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67)
	at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1676)
	at com.fasterxml.jackson.datatype.jsr310.deser.JSR310DeserializerBase._rethrowDateTimeException(JSR310DeserializerBase.java:79)
	at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer.deserialize(LocalDateTimeDeserializer.java:84)
	at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer.deserialize(LocalDateTimeDeserializer.java:39)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:530)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:528)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:417)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1287)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4202)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3205)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3173)
	at xxx.main(BulkUpdaterModel.kt:67)
	at xxx.main(BulkUpdaterModel.kt)
Caused by: java.time.format.DateTimeParseException: Text '+1000000000-01-01 00:00' could not be parsed: Invalid value for Year (valid values -999999999 - 999999999): 1000000000
	at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1920)
	at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1855)
	at java.time.LocalDateTime.parse(LocalDateTime.java:492)
	at com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer.deserialize(LocalDateTimeDeserializer.java:82)
	... 12 more
Caused by: java.time.DateTimeException: Invalid value for Year (valid values -999999999 - 999999999): 1000000000
	at java.time.temporal.ValueRange.checkValidIntValue(ValueRange.java:330)
	at java.time.temporal.ChronoField.checkValidIntValue(ChronoField.java:722)
	at java.time.chrono.IsoChronology.resolveYMD(IsoChronology.java:544)
	at java.time.chrono.IsoChronology.resolveYMD(IsoChronology.java:123)
	at java.time.chrono.AbstractChronology.resolveDate(AbstractChronology.java:472)
	at java.time.chrono.IsoChronology.resolveDate(IsoChronology.java:492)
	at java.time.chrono.IsoChronology.resolveDate(IsoChronology.java:123)
	at java.time.format.Parsed.resolveDateFields(Parsed.java:351)
	at java.time.format.Parsed.resolveFields(Parsed.java:257)
	at java.time.format.Parsed.resolve(Parsed.java:244)
	at java.time.format.DateTimeParseContext.toResolved(DateTimeParseContext.java:331)
	at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1955)
	at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
	... 14 more

@kupci
Copy link
Member

kupci commented Apr 27, 2020

Thanks for the test case. Taking a 2nd look, it seems I missed a test scenario when previously looking at this. There is some date coercion in certain cases so Cowtowncoder's thought for #166 seems to apply here as well:

since normalized date (once value normalized to UTC) would then fall outside numeric values...

@stdname
Copy link

stdname commented Apr 27, 2020

I think it's not TZ-related. Passing any year BC leads to weird behavior:
LocalDateTime.of(-10, 1,1, 1, 1) is serialized to

{"date":"0011-01-01 01:01"}

I believe that it is driven by format @JsonFormat(pattern = "yyyy-MM-dd HH:mm"). The format assumes that year can be expressed with 4 digits. Changing the pattern to @JsonFormat(pattern = "u-MM-dd HH:mm") solves the problem. Interesting question, though is why LocalDateTime.MAX is serialized correctly with both formatting patterns?
Technically speaking desired behavior is unknown, but a lot of libraries specify ISO-8601 and that is the source of confusion

@kupci
Copy link
Member

kupci commented Apr 28, 2020

Passing any year BC leads to weird behavior [emphasis added]

Good find, but figuring that would be a different but related issue? I'm not able to reproduce the issue at the moment, but might be the way we've got the test cases set up. For example, using a plain ObjectMapper, the following runs successfully:

@Test
 public void testSerializationAsTimestampBC() throws Exception
 {
     LocalDateTime time = LocalDateTime.of(-10, Month.JANUARY, 17, 15, 43);
     String value = mapper.writeValueAsString(time);

     assertNotNull("The value should not be null.", value);
     assertEquals("The value is not correct.", "[-10,1,17,15,43]", value);
 }

It sounds that you have a solution / workaround with the formatter change to @JsonFormat(pattern = "u-MM-dd HH:mm"). So focusing on MIN issue for now, and then if I can reproduce the 'any BC' issue as above (or if you have the test case, preferably in Java), I'll add that as test case, or another issue if it turns out to be a separate issue.

@cowtowncoder cowtowncoder removed the 2.12 label Dec 5, 2022
@hjed
Copy link

hjed commented Nov 5, 2024

We get the same issue when deserialising OffsetDateTimes for MIN and MAX (that were also serialised with Jackson). It can serialise it but not deserialise it.

Nevermind looks it was #166

@hjed
Copy link

hjed commented Nov 5, 2024

Actually looks like this issue does impact OffsetDateTime.MAX too:

Test:

    val objectMapper = ObjectMapper()
        .registerModules(DapiJacksonModule())
        .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
        .findAndRegisterModules()

          describe("using Instant.MAX_VALUE") {
                val dataToSerialise = TestPojo(
                lastTouched = OffsetDateTime.MAX,
            )

            it("should work") {
                val json = objectMapper.writeValueAsString(dataToSerialise)
                objectMapper.readValue(json, TestPojo::class.java) shouldBe dataToSerialise
            }
        }

data class TestPojo(
 @get:JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX", lenient = OptBoolean.TRUE)
    val lastTouched: OffsetDateTime
)

Exception: (TRUNCATED STACK TRACE AS IT IS WAY TOO LONG)

Cannot deserialize value of type `java.time.OffsetDateTime` from String "+999999999-12-31T23:59:59.999-18": Failed to deserialize java.time.OffsetDateTime: (java.time.DateTimeException) Invalid value for Year (valid values -999999999 - 999999999): 1000000000
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 49] (through reference chain: TestPojo["lastTouched"])
com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.OffsetDateTime` from String "+999999999-12-31T23:59:59.999-18": Failed to deserialize java.time.OffsetDateTime: (java.time.DateTimeException) Invalid value for Year (valid values -999999999 - 999999999): 1000000000
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 49] (through reference chain: TestPojo["lastTouched"])
	at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:67)
	at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:1959)
	at com.fasterxml.jackson.databind.DeserializationContext.handleWeirdStringValue(DeserializationContext.java:1245)
	at com.fasterxml.jackson.datatype.jsr310.deser.JSR310DeserializerBase._handleDateTimeException(JSR310DeserializerBase.java:176)
	at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer._fromString(InstantDeserializer.java:439)
	at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.java:342)
	at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.deserialize(InstantDeserializer.java:54)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:543)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:583)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:447)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1497)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:348)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:185)
	at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:342)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4917)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3860)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3828)
	at OffsetDateTimeTest$1$2$4$1.invokeSuspend(OffsetDateTimeTest.kt:130)
	at OffsetDateTimeTest$1$2$4$1.invoke(OffsetDateTimeTest.kt)
	at OffsetDateTimeTest$1$2$4$1.invoke(OffsetDateTimeTest.kt)
	at io.kotest.core.spec.style.scopes.DescribeSpecContainerScope$it$3.invokeSuspend(DescribeSpecContainerScope.kt:107)
	at io.kotest.core.spec.style.scopes.DescribeSpecContainerScope$it$3.invoke(DescribeSpecContainerScope.kt)
	at io.kotest.core.spec.style.scopes.DescribeSpecContainerScope$it$3.invoke(DescribeSpecContainerScope.kt)
	at io.kotest.engine.test.TestCaseExecutor$execute$innerExecute$1.invokeSuspend(TestCaseExecutor.kt:77)
	at io.kotest.engine.test.TestCaseExecutor$execute$innerExecute$1.invoke(TestCaseExecutor.kt)
	at io.kotest.engine.test.TestCaseExecutor$execute$innerExecute$1.invoke(TestCaseExecutor.kt)
	at io.kotest.engine.test.interceptors.CoroutineDebugProbeInterceptor.intercept(CoroutineDebugProbeInterceptor.kt:29)
	at io.kotest.engine.test.TestCaseExecutor$execute$2$1.invokeSuspend(TestCaseExecutor.kt:86)
	at io.kotest.engine.test.TestCaseExecutor$execute$2$1.invoke(TestCaseExecutor.kt)
	at io.kotest.engine.test.TestCaseExecutor$execute$2$1.invoke(TestCaseExecutor.kt)
	at 
...
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:840)
Caused by: java.time.DateTimeException: Invalid value for Year (valid values -999999999 - 999999999): 1000000000
	at java.base/java.time.temporal.ValueRange.checkValidValue(ValueRange.java:319)
	at java.base/java.time.temporal.ChronoField.checkValidValue(ChronoField.java:718)
	at java.base/java.time.LocalDate.plusDays(LocalDate.java:1384)
	at java.base/java.time.LocalDateTime.plusWithOverflow(LocalDateTime.java:1565)
	at java.base/java.time.LocalDateTime.plusSeconds(LocalDateTime.java:1322)
	at java.base/java.time.OffsetDateTime.withOffsetSameInstant(OffsetDateTime.java:703)
	at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.lambda$static$4(InstantDeserializer.java:86)
	at com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer._fromString(InstantDeserializer.java:436)
	... 344 more

I think its the same issue appologies if its not

@cowtowncoder
Copy link
Member

Separate issue for OffsetDateTime, #308, likely needs separate fix.

@hjed
Copy link

hjed commented Nov 9, 2024

#308 has a different exception to this

@cowtowncoder
Copy link
Member

@JooHyukKim Possibly similar fix as #308 (#325)?

@JooHyukKim
Copy link
Member

Wrote #328 to get reproduction, but it seems to have been fixed, probably somewhere in between 9 minor versions update.

@cowtowncoder
Copy link
Member

Added a note on #328: can merge into 2.18 or 2.17, call it fixed in whichever we choose (even if actual fix was done earlier).

@cowtowncoder cowtowncoder changed the title Issue serializing and deserializing LocalDateTime.MAX and LocalDateTime.MIN Issue serializing and deserializing LocalDateTime.MAX and LocalDateTime.MIN Nov 20, 2024
@cowtowncoder cowtowncoder added this to the 2.17.4 milestone Nov 20, 2024
cowtowncoder pushed a commit that referenced this issue Nov 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants