Нашел причину ошибки.
В документации форматируется поле типа Date, а у меня поле LocalDate.
Т.е. Freemarker поддерживает форматирование даты/времени из пакета java.util, а из пакета java.time - нет.
Нашел расширение GitHub - lazee/freemarker-java-8: Library that add java.time support to FreeMarker templates. и другие примеры добавления своих форматтеров в Freemarker, но они не работают, хотя код конфигурирования отрабатывает.
Такое ощущение, что конфигурирую не тот конфиг Feemarker, который используется, хотя он должен быть синглтоном.
Может кто-нибудь подскажет, куда дальше копать?
Пример конфигурации
@Configuration
public class FreemarkerConfig implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof FreeMarkerConfigurer configurer) {
configurer.getConfiguration().setObjectWrapper(new Java8ObjectWrapper(freemarker.template.Configuration.getVersion()));
// configurer.getConfiguration().setObjectWrapper(new CustomObjectWrapper());
/*configurer.getConfiguration().setObjectWrapper(new DefaultObjectWrapper(freemarker.template.Configuration.getVersion()) {
@Override
public TemplateModel wrap(Object object) throws TemplateModelException {
if (object instanceof LocalDate) {
return new SimpleDate(Date.valueOf((LocalDate) object));
}
if (object instanceof LocalTime) {
return new SimpleDate(Time.valueOf((LocalTime) object));
}
if (object instanceof LocalDateTime) {
return new SimpleDate(Timestamp.valueOf((LocalDateTime) object));
}
return super.wrap(object);
}
});*/
}
return bean;
}
private static class CustomObjectWrapper extends DefaultObjectWrapper {
@Override
public TemplateModel wrap(Object obj) throws TemplateModelException {
if (obj instanceof LocalDateTime) {
Timestamp timestamp = Timestamp.valueOf((LocalDateTime) obj);
return new SimpleDate(timestamp);
}
if (obj instanceof LocalDate) {
Date date = Date.valueOf((LocalDate) obj);
return new SimpleDate(date);
}
if (obj instanceof LocalTime) {
Time time = Time.valueOf((LocalTime) obj);
return new SimpleDate(time);
}
return super.wrap(obj);
}
}
}