001/* 002 * Copyright 2002-2016 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.MonthDay; 025import java.time.OffsetDateTime; 026import java.time.OffsetTime; 027import java.time.Period; 028import java.time.YearMonth; 029import java.time.ZonedDateTime; 030import java.time.format.DateTimeFormatter; 031import java.time.format.FormatStyle; 032import java.util.EnumMap; 033import java.util.Map; 034 035import org.springframework.format.FormatterRegistrar; 036import org.springframework.format.FormatterRegistry; 037import org.springframework.format.annotation.DateTimeFormat.ISO; 038import org.springframework.lang.UsesJava8; 039 040/** 041 * Configures the JSR-310 <code>java.time</code> formatting system for use with Spring. 042 * 043 * @author Juergen Hoeller 044 * @author Phillip Webb 045 * @since 4.0 046 * @see #setDateStyle 047 * @see #setTimeStyle 048 * @see #setDateTimeStyle 049 * @see #setUseIsoFormat 050 * @see org.springframework.format.FormatterRegistrar#registerFormatters 051 * @see org.springframework.format.datetime.DateFormatterRegistrar 052 * @see org.springframework.format.datetime.joda.DateTimeFormatterFactoryBean 053 */ 054@UsesJava8 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 = 064 new EnumMap<Type, DateTimeFormatter>(Type.class); 065 066 /** 067 * Factories used when specific formatters have not been specified. 068 */ 069 private final Map<Type, DateTimeFormatterFactory> factories = 070 new EnumMap<Type, DateTimeFormatterFactory>(Type.class); 071 072 073 public DateTimeFormatterRegistrar() { 074 for (Type type : Type.values()) { 075 this.factories.put(type, new DateTimeFormatterFactory()); 076 } 077 } 078 079 080 /** 081 * Set whether standard ISO formatting should be applied to all date/time types. 082 * Default is "false" (no). 083 * <p>If set to "true", the "dateStyle", "timeStyle" and "dateTimeStyle" 084 * properties are effectively ignored. 085 */ 086 public void setUseIsoFormat(boolean useIsoFormat) { 087 this.factories.get(Type.DATE).setIso(useIsoFormat ? ISO.DATE : null); 088 this.factories.get(Type.TIME).setIso(useIsoFormat ? ISO.TIME : null); 089 this.factories.get(Type.DATE_TIME).setIso(useIsoFormat ? ISO.DATE_TIME : null); 090 } 091 092 /** 093 * Set the default format style of {@link java.time.LocalDate} objects. 094 * Default is {@link java.time.format.FormatStyle#SHORT}. 095 */ 096 public void setDateStyle(FormatStyle dateStyle) { 097 this.factories.get(Type.DATE).setDateStyle(dateStyle); 098 } 099 100 /** 101 * Set the default format style of {@link java.time.LocalTime} objects. 102 * Default is {@link java.time.format.FormatStyle#SHORT}. 103 */ 104 public void setTimeStyle(FormatStyle timeStyle) { 105 this.factories.get(Type.TIME).setTimeStyle(timeStyle); 106 } 107 108 /** 109 * Set the default format style of {@link java.time.LocalDateTime} objects. 110 * Default is {@link java.time.format.FormatStyle#SHORT}. 111 */ 112 public void setDateTimeStyle(FormatStyle dateTimeStyle) { 113 this.factories.get(Type.DATE_TIME).setDateTimeStyle(dateTimeStyle); 114 } 115 116 /** 117 * Set the formatter that will be used for objects representing date values. 118 * <p>This formatter will be used for the {@link LocalDate} type. 119 * When specified, the {@link #setDateStyle dateStyle} and 120 * {@link #setUseIsoFormat useIsoFormat} properties will be ignored. 121 * @param formatter the formatter to use 122 * @see #setTimeFormatter 123 * @see #setDateTimeFormatter 124 */ 125 public void setDateFormatter(DateTimeFormatter formatter) { 126 this.formatters.put(Type.DATE, formatter); 127 } 128 129 /** 130 * Set the formatter that will be used for objects representing time values. 131 * <p>This formatter will be used for the {@link LocalTime} and {@link OffsetTime} 132 * types. When specified, the {@link #setTimeStyle timeStyle} and 133 * {@link #setUseIsoFormat useIsoFormat} properties will be ignored. 134 * @param formatter the formatter to use 135 * @see #setDateFormatter 136 * @see #setDateTimeFormatter 137 */ 138 public void setTimeFormatter(DateTimeFormatter formatter) { 139 this.formatters.put(Type.TIME, formatter); 140 } 141 142 /** 143 * Set the formatter that will be used for objects representing date and time values. 144 * <p>This formatter will be used for {@link LocalDateTime}, {@link ZonedDateTime} 145 * and {@link OffsetDateTime} types. When specified, the 146 * {@link #setDateTimeStyle dateTimeStyle} and 147 * {@link #setUseIsoFormat useIsoFormat} properties will be ignored. 148 * @param formatter the formatter to use 149 * @see #setDateFormatter 150 * @see #setTimeFormatter 151 */ 152 public void setDateTimeFormatter(DateTimeFormatter formatter) { 153 this.formatters.put(Type.DATE_TIME, formatter); 154 } 155 156 157 @Override 158 public void registerFormatters(FormatterRegistry registry) { 159 DateTimeConverters.registerConverters(registry); 160 161 DateTimeFormatter df = getFormatter(Type.DATE); 162 DateTimeFormatter tf = getFormatter(Type.TIME); 163 DateTimeFormatter dtf = getFormatter(Type.DATE_TIME); 164 165 // Efficient ISO_LOCAL_* variants for printing since they are twice as fast... 166 167 registry.addFormatterForFieldType(LocalDate.class, 168 new TemporalAccessorPrinter( 169 df == DateTimeFormatter.ISO_DATE ? DateTimeFormatter.ISO_LOCAL_DATE : df), 170 new TemporalAccessorParser(LocalDate.class, df)); 171 172 registry.addFormatterForFieldType(LocalTime.class, 173 new TemporalAccessorPrinter( 174 tf == DateTimeFormatter.ISO_TIME ? DateTimeFormatter.ISO_LOCAL_TIME : tf), 175 new TemporalAccessorParser(LocalTime.class, tf)); 176 177 registry.addFormatterForFieldType(LocalDateTime.class, 178 new TemporalAccessorPrinter( 179 dtf == DateTimeFormatter.ISO_DATE_TIME ? DateTimeFormatter.ISO_LOCAL_DATE_TIME : dtf), 180 new TemporalAccessorParser(LocalDateTime.class, dtf)); 181 182 registry.addFormatterForFieldType(ZonedDateTime.class, 183 new TemporalAccessorPrinter(dtf), 184 new TemporalAccessorParser(ZonedDateTime.class, dtf)); 185 186 registry.addFormatterForFieldType(OffsetDateTime.class, 187 new TemporalAccessorPrinter(dtf), 188 new TemporalAccessorParser(OffsetDateTime.class, dtf)); 189 190 registry.addFormatterForFieldType(OffsetTime.class, 191 new TemporalAccessorPrinter(tf), 192 new TemporalAccessorParser(OffsetTime.class, tf)); 193 194 registry.addFormatterForFieldType(Instant.class, new InstantFormatter()); 195 registry.addFormatterForFieldType(Period.class, new PeriodFormatter()); 196 registry.addFormatterForFieldType(Duration.class, new DurationFormatter()); 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}