Skip to content

Commit 95e5d4e

Browse files
committed
Polish "Enable users to provide custom time and datetime formats"
See gh-18772
1 parent 69b51cd commit 95e5d4e

File tree

9 files changed

+390
-129
lines changed

9 files changed

+390
-129
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.web.format;
18+
19+
import java.time.format.DateTimeFormatter;
20+
import java.time.format.ResolverStyle;
21+
22+
import org.springframework.util.StringUtils;
23+
24+
/**
25+
* {@link DateTimeFormatter Formatters} for dates, times, and date-times.
26+
*
27+
* @author Andy Wilkinson
28+
* @since 2.3.0
29+
*/
30+
public class DateTimeFormatters {
31+
32+
private DateTimeFormatter dateFormatter;
33+
34+
private String datePattern;
35+
36+
private DateTimeFormatter timeFormatter;
37+
38+
private DateTimeFormatter dateTimeFormatter;
39+
40+
/**
41+
* Configures the date format using the given {@code pattern}.
42+
* @param pattern the pattern for formatting dates
43+
* @return {@code this} for chained method invocation
44+
*/
45+
public DateTimeFormatters dateFormat(String pattern) {
46+
this.dateFormatter = formatter(pattern);
47+
this.datePattern = pattern;
48+
return this;
49+
}
50+
51+
/**
52+
* Configures the time format using the given {@code pattern}.
53+
* @param pattern the pattern for formatting times
54+
* @return {@code this} for chained method invocation
55+
*/
56+
public DateTimeFormatters timeFormat(String pattern) {
57+
this.timeFormatter = formatter(pattern);
58+
return this;
59+
}
60+
61+
/**
62+
* Configures the date-time format using the given {@code pattern}.
63+
* @param pattern the pattern for formatting date-times
64+
* @return {@code this} for chained method invocation
65+
*/
66+
public DateTimeFormatters dateTimeFormat(String pattern) {
67+
this.dateTimeFormatter = formatter(pattern);
68+
return this;
69+
}
70+
71+
DateTimeFormatter getDateFormatter() {
72+
return this.dateFormatter;
73+
}
74+
75+
String getDatePattern() {
76+
return this.datePattern;
77+
}
78+
79+
DateTimeFormatter getTimeFormatter() {
80+
return this.timeFormatter;
81+
}
82+
83+
DateTimeFormatter getDateTimeFormatter() {
84+
return this.dateTimeFormatter;
85+
}
86+
87+
boolean isCustomized() {
88+
return this.dateFormatter != null || this.timeFormatter != null || this.dateTimeFormatter != null;
89+
}
90+
91+
private static DateTimeFormatter formatter(String pattern) {
92+
return StringUtils.hasText(pattern)
93+
? DateTimeFormatter.ofPattern(pattern).withResolverStyle(ResolverStyle.SMART) : null;
94+
}
95+
96+
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/format/WebConversionService.java

Lines changed: 32 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
package org.springframework.boot.autoconfigure.web.format;
1818

1919
import java.time.format.DateTimeFormatter;
20-
import java.time.format.ResolverStyle;
20+
import java.util.function.Consumer;
21+
import java.util.function.Supplier;
2122

2223
import org.springframework.format.datetime.DateFormatter;
2324
import org.springframework.format.datetime.DateFormatterRegistrar;
@@ -28,7 +29,6 @@
2829
import org.springframework.format.number.money.MonetaryAmountFormatter;
2930
import org.springframework.format.support.DefaultFormattingConversionService;
3031
import org.springframework.util.ClassUtils;
31-
import org.springframework.util.StringUtils;
3232

3333
/**
3434
* {@link org.springframework.format.support.FormattingConversionService} dedicated to web
@@ -46,83 +46,70 @@ public class WebConversionService extends DefaultFormattingConversionService {
4646
private static final boolean JSR_354_PRESENT = ClassUtils.isPresent("javax.money.MonetaryAmount",
4747
WebConversionService.class.getClassLoader());
4848

49-
private final String dateFormat;
50-
51-
private final String timeFormat;
52-
53-
private final String dateTimeFormat;
54-
5549
/**
5650
* Create a new WebConversionService that configures formatters with the provided date
5751
* format, or register the default ones if no custom format is provided.
5852
* @param dateFormat the custom date format to use for date conversions
53+
* @deprecated since 2.3.0 in favor of
54+
* {@link #WebConversionService(DateTimeFormatters)}
5955
*/
56+
@Deprecated
6057
public WebConversionService(String dateFormat) {
61-
this(dateFormat, null, null);
58+
this(new DateTimeFormatters().dateFormat(dateFormat));
6259
}
6360

6461
/**
65-
* Create a new WebConversionService that configures formatters with the provided date
66-
* and time formats, or register the default ones if no custom formats are provided.
67-
* @param dateFormat the custom date format to use for date conversions
68-
* @param timeFormat the custom time format to use for time conversions
69-
* @param dateTimeFormat the custom datetime format to use for datetime conversions
62+
* Create a new WebConversionService that configures formatters with the provided
63+
* date, time, and date-time formats, or registers the default if no custom format is
64+
* provided.
65+
* @param dateTimeFormatters the formatters to use for date, time, and date-time
66+
* formatting
67+
* @since 2.3.0
7068
*/
71-
public WebConversionService(String dateFormat, String timeFormat, String dateTimeFormat) {
69+
public WebConversionService(DateTimeFormatters dateTimeFormatters) {
7270
super(false);
73-
this.dateFormat = getNonEmptyFormat(dateFormat);
74-
this.timeFormat = getNonEmptyFormat(timeFormat);
75-
this.dateTimeFormat = getNonEmptyFormat(dateTimeFormat);
76-
if (this.dateFormat != null || this.timeFormat != null || this.dateTimeFormat != null) {
77-
addFormatters();
71+
if (dateTimeFormatters.isCustomized()) {
72+
addFormatters(dateTimeFormatters);
7873
}
7974
else {
8075
addDefaultFormatters(this);
8176
}
8277
}
8378

84-
private void addFormatters() {
79+
private void addFormatters(DateTimeFormatters dateTimeFormatters) {
8580
addFormatterForFieldAnnotation(new NumberFormatAnnotationFormatterFactory());
8681
if (JSR_354_PRESENT) {
8782
addFormatter(new CurrencyUnitFormatter());
8883
addFormatter(new MonetaryAmountFormatter());
8984
addFormatterForFieldAnnotation(new Jsr354NumberFormatAnnotationFormatterFactory());
9085
}
91-
registerJsr310();
92-
registerJavaDate();
86+
registerJsr310(dateTimeFormatters);
87+
registerJavaDate(dateTimeFormatters);
9388
}
9489

95-
private void registerJsr310() {
90+
private void registerJsr310(DateTimeFormatters dateTimeFormatters) {
9691
DateTimeFormatterRegistrar dateTime = new DateTimeFormatterRegistrar();
97-
if (this.dateFormat != null) {
98-
dateTime.setDateFormatter(
99-
DateTimeFormatter.ofPattern(this.dateFormat).withResolverStyle(ResolverStyle.SMART));
100-
}
101-
102-
if (this.timeFormat != null) {
103-
dateTime.setTimeFormatter(
104-
DateTimeFormatter.ofPattern(this.timeFormat).withResolverStyle(ResolverStyle.SMART));
105-
}
92+
configure(dateTimeFormatters::getDateFormatter, dateTime::setDateFormatter);
93+
configure(dateTimeFormatters::getTimeFormatter, dateTime::setTimeFormatter);
94+
configure(dateTimeFormatters::getDateTimeFormatter, dateTime::setDateTimeFormatter);
95+
dateTime.registerFormatters(this);
96+
}
10697

107-
if (this.dateTimeFormat != null) {
108-
dateTime.setDateTimeFormatter(
109-
DateTimeFormatter.ofPattern(this.dateTimeFormat).withResolverStyle(ResolverStyle.SMART));
98+
private void configure(Supplier<DateTimeFormatter> supplier, Consumer<DateTimeFormatter> consumer) {
99+
DateTimeFormatter formatter = supplier.get();
100+
if (formatter != null) {
101+
consumer.accept(formatter);
110102
}
111-
112-
dateTime.registerFormatters(this);
113103
}
114104

115-
private void registerJavaDate() {
105+
private void registerJavaDate(DateTimeFormatters dateTimeFormatters) {
116106
DateFormatterRegistrar dateFormatterRegistrar = new DateFormatterRegistrar();
117-
if (this.dateFormat != null) {
118-
DateFormatter dateFormatter = new DateFormatter(this.dateFormat);
107+
String datePattern = dateTimeFormatters.getDatePattern();
108+
if (datePattern != null) {
109+
DateFormatter dateFormatter = new DateFormatter(datePattern);
119110
dateFormatterRegistrar.setFormatter(dateFormatter);
120111
}
121112
dateFormatterRegistrar.registerFormatters(this);
122113
}
123114

124-
private static String getNonEmptyFormat(final String format) {
125-
return StringUtils.hasText(format) ? format : null;
126-
}
127-
128115
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
import org.springframework.boot.autoconfigure.validation.ValidatorAdapter;
3636
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
3737
import org.springframework.boot.autoconfigure.web.ResourceProperties;
38+
import org.springframework.boot.autoconfigure.web.format.DateTimeFormatters;
3839
import org.springframework.boot.autoconfigure.web.format.WebConversionService;
40+
import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties.Format;
3941
import org.springframework.boot.context.properties.EnableConfigurationProperties;
4042
import org.springframework.boot.convert.ApplicationConversionService;
4143
import org.springframework.boot.web.codec.CodecCustomizer;
@@ -203,8 +205,9 @@ public EnableWebFluxConfiguration(WebFluxProperties webFluxProperties,
203205
@Bean
204206
@Override
205207
public FormattingConversionService webFluxConversionService() {
206-
WebConversionService conversionService = new WebConversionService(this.webFluxProperties.getDateFormat(),
207-
this.webFluxProperties.getTimeFormat(), this.webFluxProperties.getDateTimeFormat());
208+
Format format = this.webFluxProperties.getFormat();
209+
WebConversionService conversionService = new WebConversionService(new DateTimeFormatters()
210+
.dateFormat(format.getDate()).timeFormat(format.getTime()).dateTimeFormat(format.getDateTime()));
208211
addFormatters(conversionService);
209212
return conversionService;
210213
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxProperties.java

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.boot.autoconfigure.web.reactive;
1818

1919
import org.springframework.boot.context.properties.ConfigurationProperties;
20+
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
2021
import org.springframework.util.StringUtils;
2122

2223
/**
@@ -33,20 +34,7 @@ public class WebFluxProperties {
3334
*/
3435
private String basePath;
3536

36-
/**
37-
* Date format to use. For instance, `dd/MM/yyyy`.
38-
*/
39-
private String dateFormat;
40-
41-
/**
42-
* Time format to use. For instance, `HH:mm:ss`.
43-
*/
44-
private String timeFormat;
45-
46-
/**
47-
* Datetime format to use. For instance, `yyyy-MM-dd HH:mm:ss`.
48-
*/
49-
private String dateTimeFormat;
37+
private final Format format = new Format();
5038

5139
/**
5240
* Path pattern used for static resources.
@@ -74,28 +62,19 @@ private String cleanBasePath(String basePath) {
7462
return candidate;
7563
}
7664

65+
@Deprecated
66+
@DeprecatedConfigurationProperty(replacement = "spring.webflux.format.date")
7767
public String getDateFormat() {
78-
return this.dateFormat;
68+
return this.format.getDate();
7969
}
8070

71+
@Deprecated
8172
public void setDateFormat(String dateFormat) {
82-
this.dateFormat = dateFormat;
73+
this.format.setDate(dateFormat);
8374
}
8475

85-
public String getTimeFormat() {
86-
return this.timeFormat;
87-
}
88-
89-
public void setTimeFormat(final String timeFormat) {
90-
this.timeFormat = timeFormat;
91-
}
92-
93-
public String getDateTimeFormat() {
94-
return this.dateTimeFormat;
95-
}
96-
97-
public void setDateTimeFormat(final String dateTimeFormat) {
98-
this.dateTimeFormat = dateTimeFormat;
76+
public Format getFormat() {
77+
return this.format;
9978
}
10079

10180
public String getStaticPathPattern() {
@@ -106,4 +85,47 @@ public void setStaticPathPattern(String staticPathPattern) {
10685
this.staticPathPattern = staticPathPattern;
10786
}
10887

88+
public static class Format {
89+
90+
/**
91+
* Date format to use, for example `dd/MM/yyyy`.
92+
*/
93+
private String date;
94+
95+
/**
96+
* Time format to use, for example `HH:mm:ss`.
97+
*/
98+
private String time;
99+
100+
/**
101+
* Date-time format to use, for example `yyyy-MM-dd HH:mm:ss`.
102+
*/
103+
private String dateTime;
104+
105+
public String getDate() {
106+
return this.date;
107+
}
108+
109+
public void setDate(String date) {
110+
this.date = date;
111+
}
112+
113+
public String getTime() {
114+
return this.time;
115+
}
116+
117+
public void setTime(String time) {
118+
this.time = time;
119+
}
120+
121+
public String getDateTime() {
122+
return this.dateTime;
123+
}
124+
125+
public void setDateTime(String dateTime) {
126+
this.dateTime = dateTime;
127+
}
128+
129+
}
130+
109131
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@
5151
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
5252
import org.springframework.boot.autoconfigure.web.ResourceProperties;
5353
import org.springframework.boot.autoconfigure.web.ResourceProperties.Strategy;
54+
import org.springframework.boot.autoconfigure.web.format.DateTimeFormatters;
5455
import org.springframework.boot.autoconfigure.web.format.WebConversionService;
56+
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.Format;
5557
import org.springframework.boot.context.properties.EnableConfigurationProperties;
5658
import org.springframework.boot.convert.ApplicationConversionService;
5759
import org.springframework.boot.web.servlet.filter.OrderedFormContentFilter;
@@ -427,8 +429,9 @@ private boolean isReadable(Resource resource) {
427429
@Bean
428430
@Override
429431
public FormattingConversionService mvcConversionService() {
430-
WebConversionService conversionService = new WebConversionService(this.mvcProperties.getDateFormat(),
431-
this.mvcProperties.getTimeFormat(), this.mvcProperties.getDateTimeFormat());
432+
Format format = this.mvcProperties.getFormat();
433+
WebConversionService conversionService = new WebConversionService(new DateTimeFormatters()
434+
.dateFormat(format.getDate()).timeFormat(format.getTime()).dateTimeFormat(format.getDateTime()));
432435
addFormatters(conversionService);
433436
return conversionService;
434437
}

0 commit comments

Comments
 (0)