Affects: 5.3.21
Background
TimeZone is Asia/Singapore snakeyml 1.31 spring-boot 2.6.9
Code
import java.util.Date;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
@Data
public class TestDateTime {
@Value("${test.date:2022-10-13 00:00:00}")
private Date test;
}
application.yml
test:
date: 2022-10-13 00:00:00
Error
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testDateTime': Unsatisfied dependency expressed through field 'test'; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.beans.factory.annotation.Value java.util.Date] for value 'Thu Oct 13 08:00:00 SGT 2022'; nested exception is java.lang.IllegalArgumentException
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:659) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:639) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.21.jar:5.3.21]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.21.jar:5.3.21]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[spring-boot-2.6.9.jar:2.6.9]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:745) [spring-boot-2.6.9.jar:2.6.9]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:420) [spring-boot-2.6.9.jar:2.6.9]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-2.6.9.jar:2.6.9]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1317) [spring-boot-2.6.9.jar:2.6.9]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) [spring-boot-2.6.9.jar:2.6.9]
at com.jpan.objectMapper.demo.ObjectMapperApplication.main(ObjectMapperApplication.java:13) [classes/:na]
Caused by: org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.beans.factory.annotation.Value java.util.Date] for value 'Thu Oct 13 08:00:00 SGT 2022'; nested exception is java.lang.IllegalArgumentException
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:79) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1339) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:656) ~[spring-beans-5.3.21.jar:5.3.21]
... 20 common frames omitted
Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.beans.factory.annotation.Value java.util.Date] for value 'Thu Oct 13 08:00:00 SGT 2022'; nested exception is java.lang.IllegalArgumentException
at org.springframework.core.convert.support.ObjectToObjectConverter.convert(ObjectToObjectConverter.java:119) ~[spring-core-5.3.21.jar:5.3.21]
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41) ~[spring-core-5.3.21.jar:5.3.21]
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:192) ~[spring-core-5.3.21.jar:5.3.21]
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:129) ~[spring-beans-5.3.21.jar:5.3.21]
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:73) ~[spring-beans-5.3.21.jar:5.3.21]
... 23 common frames omitted
Caused by: java.lang.IllegalArgumentException: null
at java.util.Date.parse(Date.java:617) ~[na:1.8.0_292]
at java.util.Date.<init>(Date.java:274) ~[na:1.8.0_292]
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_292]
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_292]
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_292]
at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_292]
at org.springframework.core.convert.support.ObjectToObjectConverter.convert(ObjectToObjectConverter.java:115) ~[spring-core-5.3.21.jar:5.3.21]
... 27 common frames omitted
Disconnected from the target VM, address: '127.0.0.1:53797', transport: 'socket'
Process finished with exit code 1
Reason
When getting the configuration from snakeyml 1.31, the value of test.date
is "Thu Oct 13 08:00:00 SGT 2022", meanwhile java.util.Date#parse()
cannot resolve SGT time zone.
Comment From: snicoll
I believe the value in your YAML configuration needs to be quoted. Can you please try that?
Comment From: Linda-pan
if set config
test:
date: 2022-10-18 00:00:00
then error log shows
Failed to convert from type [java.lang.String] to type [@org.springframework.beans.factory.annotation.Value java.util.Date] for value 'Tue Oct 18 08:00:00 SGT 2022';
if set config
test:
date: "2022-10-18 00:00:00"
then error log shows
Failed to convert from type [java.lang.String] to type [@org.springframework.beans.factory.annotation.Value java.util.Date] for value '2022-10-18 00:00:00'';
if not config and set default value to @Value。
@Value("${test.date:2022-10-13 00:00:00}")
then error log shows
Failed to convert from type [java.lang.String] to type [@org.springframework.beans.factory.annotation.Value java.util.Date] for value '2022-10-13 00:00:00'
I think there are two problems, one is that when the @Value is in a bean, the bean cannot support the default date value in the format "yyyy-MM-dd HH:mm:ss". Another is when the @Value is in a bean and region is similar to "Asia/Singapore",the time with timezone from environment config which resolved by snakeyaml may cause a parsing error.
These are pivotal code:
get value with timezone
org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency(DefaultListableBeanFactory:1332)
1331 if (value instanceof String) {
1332 String strVal = resolveEmbeddedValue((String) value);
1333 BeanDefinition bd = (beanName != null && containsBean(beanName) ?
1334 getMergedBeanDefinition(beanName) : null);
1335 value = evaluateBeanDefinitionString(strVal, bd);
1336 }
and create Instance
org.springframework.core.convert.support.ObjectToObjectConverter#convert(ObjectToObjectConverter.java:115)
112 else if (executable instanceof Constructor) {
113 Constructor<?> ctor = (Constructor<?>) executable;
114 ReflectionUtils.makeAccessible(ctor);
115 return ctor.newInstance(source);
116 }
then go to java.util.Date#parse
Comment From: huifer
@Linda-pan Hi ,I added a unit test, you can look at this unit test, after testing can be found to be normal conversion。Since there are many types of time formats("yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd",) I am not sure if it is necessary to include the SampleDatePropertyEditor code in the test case as part of the Spring core code. @snicoll
The address of my updated unit test code: https://github.com/huifer/spring-framework/tree/issues-29314
Comment From: snicoll
Thanks for helping out @huifer.
@Linda-pan Yes so it turns out that injecting a Date
the way you do don't specify the date format that you want to use so it uses the default. If you want control over the date format, you need to add @DateTimeFormat
and configure the pattern to use, something like:
public class TestDateTime {
@Value("${test.date:2022-10-13 00:00:00}")
@DateTimeFormat(pattern = "<format-here>")
private Date test;
}