001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.format.datetime.standard; 018 019import java.time.Duration; 020import java.time.Instant; 021import java.time.LocalDate; 022import java.time.LocalDateTime; 023import java.time.LocalTime; 024import java.time.Month; 025import java.time.MonthDay; 026import java.time.OffsetDateTime; 027import java.time.OffsetTime; 028import java.time.Period; 029import java.time.Year; 030import java.time.YearMonth; 031import java.time.ZonedDateTime; 032import java.time.format.DateTimeFormatter; 033import java.time.format.FormatStyle; 034import java.util.EnumMap; 035import java.util.Map; 036 037import org.springframework.format.FormatterRegistrar; 038import org.springframework.format.FormatterRegistry; 039import org.springframework.format.annotation.DateTimeFormat.ISO; 040 041/** 042 * Configures the JSR-310 <code>java.time</code> formatting system for use with Spring. 043 * 044 * @author Juergen Hoeller 045 * @author Phillip Webb 046 * @since 4.0 047 * @see #setDateStyle 048 * @see #setTimeStyle 049 * @see #setDateTimeStyle 050 * @see #setUseIsoFormat 051 * @see org.springframework.format.FormatterRegistrar#registerFormatters 052 * @see org.springframework.format.datetime.DateFormatterRegistrar 053 * @see org.springframework.format.datetime.joda.DateTimeFormatterFactoryBean 054 */ 055public class DateTimeFormatterRegistrar implements FormatterRegistrar { 056 057 private enum Type {DATE, TIME, DATE_TIME} 058 059 060 /** 061 * User-defined formatters. 062 */ 063 private final Map<Type, DateTimeFormatter> formatters = new EnumMap<>(Type.class); 064 065 /** 066 * Factories used when specific formatters have not been specified. 067 */ 068 private final Map<Type, DateTimeFormatterFactory> factories = new EnumMap<>(Type.class); 069 070 071 public DateTimeFormatterRegistrar() { 072 for (Type type : Type.values()) { 073 this.factories.put(type, new DateTimeFormatterFactory()); 074 } 075 } 076 077 078 /** 079 * Set whether standard ISO formatting should be applied to all date/time types. 080 * Default is "false" (no). 081 * <p>If set to "true", the "dateStyle", "timeStyle" and "dateTimeStyle" 082 * properties are effectively ignored. 083 */ 084 public void setUseIsoFormat(boolean useIsoFormat) { 085 this.factories.get(Type.DATE).setIso(useIsoFormat ? ISO.DATE : ISO.NONE); 086 this.factories.get(Type.TIME).setIso(useIsoFormat ? ISO.TIME : ISO.NONE); 087 this.factories.get(Type.DATE_TIME).setIso(useIsoFormat ? ISO.DATE_TIME : ISO.NONE); 088 } 089 090 /** 091 * Set the default format style of {@link java.time.LocalDate} objects. 092 * Default is {@link java.time.format.FormatStyle#SHORT}. 093 */ 094 public void setDateStyle(FormatStyle dateStyle) { 095 this.factories.get(Type.DATE).setDateStyle(dateStyle); 096 } 097 098 /** 099 * Set the default format style of {@link java.time.LocalTime} objects. 100 * Default is {@link java.time.format.FormatStyle#SHORT}. 101 */ 102 public void setTimeStyle(FormatStyle timeStyle) { 103 this.factories.get(Type.TIME).setTimeStyle(timeStyle); 104 } 105 106 /** 107 * Set the default format style of {@link java.time.LocalDateTime} objects. 108 * Default is {@link java.time.format.FormatStyle#SHORT}. 109 */ 110 public void setDateTimeStyle(FormatStyle dateTimeStyle) { 111 this.factories.get(Type.DATE_TIME).setDateTimeStyle(dateTimeStyle); 112 } 113 114 /** 115 * Set the formatter that will be used for objects representing date values. 116 * <p>This formatter will be used for the {@link LocalDate} type. 117 * When specified, the {@link #setDateStyle dateStyle} and 118 * {@link #setUseIsoFormat useIsoFormat} properties will be ignored. 119 * @param formatter the formatter to use 120 * @see #setTimeFormatter 121 * @see #setDateTimeFormatter 122 */ 123 public void setDateFormatter(DateTimeFormatter formatter) { 124 this.formatters.put(Type.DATE, formatter); 125 } 126 127 /** 128 * Set the formatter that will be used for objects representing time values. 129 * <p>This formatter will be used for the {@link LocalTime} and {@link OffsetTime} 130 * types. When specified, the {@link #setTimeStyle timeStyle} and 131 * {@link #setUseIsoFormat useIsoFormat} properties will be ignored. 132 * @param formatter the formatter to use 133 * @see #setDateFormatter 134 * @see #setDateTimeFormatter 135 */ 136 public void setTimeFormatter(DateTimeFormatter formatter) { 137 this.formatters.put(Type.TIME, formatter); 138 } 139 140 /** 141 * Set the formatter that will be used for objects representing date and time values. 142 * <p>This formatter will be used for {@link LocalDateTime}, {@link ZonedDateTime} 143 * and {@link OffsetDateTime} types. When specified, the 144 * {@link #setDateTimeStyle dateTimeStyle} and 145 * {@link #setUseIsoFormat useIsoFormat} properties will be ignored. 146 * @param formatter the formatter to use 147 * @see #setDateFormatter 148 * @see #setTimeFormatter 149 */ 150 public void setDateTimeFormatter(DateTimeFormatter formatter) { 151 this.formatters.put(Type.DATE_TIME, formatter); 152 } 153 154 155 @Override 156 public void registerFormatters(FormatterRegistry registry) { 157 DateTimeConverters.registerConverters(registry); 158 159 DateTimeFormatter df = getFormatter(Type.DATE); 160 DateTimeFormatter tf = getFormatter(Type.TIME); 161 DateTimeFormatter dtf = getFormatter(Type.DATE_TIME); 162 163 // Efficient ISO_LOCAL_* variants for printing since they are twice as fast... 164 165 registry.addFormatterForFieldType(LocalDate.class, 166 new TemporalAccessorPrinter( 167 df == DateTimeFormatter.ISO_DATE ? DateTimeFormatter.ISO_LOCAL_DATE : df), 168 new TemporalAccessorParser(LocalDate.class, df)); 169 170 registry.addFormatterForFieldType(LocalTime.class, 171 new TemporalAccessorPrinter( 172 tf == DateTimeFormatter.ISO_TIME ? DateTimeFormatter.ISO_LOCAL_TIME : tf), 173 new TemporalAccessorParser(LocalTime.class, tf)); 174 175 registry.addFormatterForFieldType(LocalDateTime.class, 176 new TemporalAccessorPrinter( 177 dtf == DateTimeFormatter.ISO_DATE_TIME ? DateTimeFormatter.ISO_LOCAL_DATE_TIME : dtf), 178 new TemporalAccessorParser(LocalDateTime.class, dtf)); 179 180 registry.addFormatterForFieldType(ZonedDateTime.class, 181 new TemporalAccessorPrinter(dtf), 182 new TemporalAccessorParser(ZonedDateTime.class, dtf)); 183 184 registry.addFormatterForFieldType(OffsetDateTime.class, 185 new TemporalAccessorPrinter(dtf), 186 new TemporalAccessorParser(OffsetDateTime.class, dtf)); 187 188 registry.addFormatterForFieldType(OffsetTime.class, 189 new TemporalAccessorPrinter(tf), 190 new TemporalAccessorParser(OffsetTime.class, tf)); 191 192 registry.addFormatterForFieldType(Instant.class, new InstantFormatter()); 193 registry.addFormatterForFieldType(Period.class, new PeriodFormatter()); 194 registry.addFormatterForFieldType(Duration.class, new DurationFormatter()); 195 registry.addFormatterForFieldType(Year.class, new YearFormatter()); 196 registry.addFormatterForFieldType(Month.class, new MonthFormatter()); 197 registry.addFormatterForFieldType(YearMonth.class, new YearMonthFormatter()); 198 registry.addFormatterForFieldType(MonthDay.class, new MonthDayFormatter()); 199 200 registry.addFormatterForFieldAnnotation(new Jsr310DateTimeFormatAnnotationFormatterFactory()); 201 } 202 203 private DateTimeFormatter getFormatter(Type type) { 204 DateTimeFormatter formatter = this.formatters.get(type); 205 if (formatter != null) { 206 return formatter; 207 } 208 DateTimeFormatter fallbackFormatter = getFallbackFormatter(type); 209 return this.factories.get(type).createDateTimeFormatter(fallbackFormatter); 210 } 211 212 private DateTimeFormatter getFallbackFormatter(Type type) { 213 switch (type) { 214 case DATE: return DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT); 215 case TIME: return DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT); 216 default: return DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT); 217 } 218 } 219 220}