/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.format.datetime;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.EnumMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

import org.springframework.format.Formatter;
import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;

A formatter for Date types. Allows the configuration of an explicit date pattern and locale.
Author:Keith Donald, Juergen Hoeller, Phillip Webb
See Also:
Since:3.0
/** * A formatter for {@link java.util.Date} types. * Allows the configuration of an explicit date pattern and locale. * * @author Keith Donald * @author Juergen Hoeller * @author Phillip Webb * @since 3.0 * @see SimpleDateFormat */
public class DateFormatter implements Formatter<Date> { private static final TimeZone UTC = TimeZone.getTimeZone("UTC"); private static final Map<ISO, String> ISO_PATTERNS; static { Map<ISO, String> formats = new EnumMap<>(ISO.class); formats.put(ISO.DATE, "yyyy-MM-dd"); formats.put(ISO.TIME, "HH:mm:ss.SSSXXX"); formats.put(ISO.DATE_TIME, "yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); ISO_PATTERNS = Collections.unmodifiableMap(formats); } @Nullable private String pattern; private int style = DateFormat.DEFAULT; @Nullable private String stylePattern; @Nullable private ISO iso; @Nullable private TimeZone timeZone; private boolean lenient = false;
Create a new default DateFormatter.
/** * Create a new default DateFormatter. */
public DateFormatter() { }
Create a new DateFormatter for the given date pattern.
/** * Create a new DateFormatter for the given date pattern. */
public DateFormatter(String pattern) { this.pattern = pattern; }
Set the pattern to use to format date values.

If not specified, DateFormat's default style will be used.

/** * Set the pattern to use to format date values. * <p>If not specified, DateFormat's default style will be used. */
public void setPattern(String pattern) { this.pattern = pattern; }
Set the ISO format used for this date.
Params:
  • iso – the ISO format
Since:3.2
/** * Set the ISO format used for this date. * @param iso the {@link ISO} format * @since 3.2 */
public void setIso(ISO iso) { this.iso = iso; }
Set the style to use to format date values.

If not specified, DateFormat's default style will be used.

See Also:
/** * Set the style to use to format date values. * <p>If not specified, DateFormat's default style will be used. * @see DateFormat#DEFAULT * @see DateFormat#SHORT * @see DateFormat#MEDIUM * @see DateFormat#LONG * @see DateFormat#FULL */
public void setStyle(int style) { this.style = style; }
Set the two character to use to format date values. The first character used for the date style, the second is for the time style. Supported characters are
  • 'S' = Small
  • 'M' = Medium
  • 'L' = Long
  • 'F' = Full
  • '-' = Omitted
This method mimics the styles supported by Joda-Time.
Params:
  • stylePattern – two characters from the set {"S", "M", "L", "F", "-"}
Since:3.2
/** * Set the two character to use to format date values. The first character used for * the date style, the second is for the time style. Supported characters are * <ul> * <li>'S' = Small</li> * <li>'M' = Medium</li> * <li>'L' = Long</li> * <li>'F' = Full</li> * <li>'-' = Omitted</li> * </ul> * This method mimics the styles supported by Joda-Time. * @param stylePattern two characters from the set {"S", "M", "L", "F", "-"} * @since 3.2 */
public void setStylePattern(String stylePattern) { this.stylePattern = stylePattern; }
Set the TimeZone to normalize the date values into, if any.
/** * Set the TimeZone to normalize the date values into, if any. */
public void setTimeZone(TimeZone timeZone) { this.timeZone = timeZone; }
Specify whether or not parsing is to be lenient. Default is false.

With lenient parsing, the parser may allow inputs that do not precisely match the format. With strict parsing, inputs must match the format exactly.

/** * Specify whether or not parsing is to be lenient. Default is false. * <p>With lenient parsing, the parser may allow inputs that do not precisely match the format. * With strict parsing, inputs must match the format exactly. */
public void setLenient(boolean lenient) { this.lenient = lenient; } @Override public String print(Date date, Locale locale) { return getDateFormat(locale).format(date); } @Override public Date parse(String text, Locale locale) throws ParseException { return getDateFormat(locale).parse(text); } protected DateFormat getDateFormat(Locale locale) { DateFormat dateFormat = createDateFormat(locale); if (this.timeZone != null) { dateFormat.setTimeZone(this.timeZone); } dateFormat.setLenient(this.lenient); return dateFormat; } private DateFormat createDateFormat(Locale locale) { if (StringUtils.hasLength(this.pattern)) { return new SimpleDateFormat(this.pattern, locale); } if (this.iso != null && this.iso != ISO.NONE) { String pattern = ISO_PATTERNS.get(this.iso); if (pattern == null) { throw new IllegalStateException("Unsupported ISO format " + this.iso); } SimpleDateFormat format = new SimpleDateFormat(pattern); format.setTimeZone(UTC); return format; } if (StringUtils.hasLength(this.stylePattern)) { int dateStyle = getStylePatternForChar(0); int timeStyle = getStylePatternForChar(1); if (dateStyle != -1 && timeStyle != -1) { return DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale); } if (dateStyle != -1) { return DateFormat.getDateInstance(dateStyle, locale); } if (timeStyle != -1) { return DateFormat.getTimeInstance(timeStyle, locale); } throw new IllegalStateException("Unsupported style pattern '" + this.stylePattern + "'"); } return DateFormat.getDateInstance(this.style, locale); } private int getStylePatternForChar(int index) { if (this.stylePattern != null && this.stylePattern.length() > index) { switch (this.stylePattern.charAt(index)) { case 'S': return DateFormat.SHORT; case 'M': return DateFormat.MEDIUM; case 'L': return DateFormat.LONG; case 'F': return DateFormat.FULL; case '-': return -1; } } throw new IllegalStateException("Unsupported style pattern '" + this.stylePattern + "'"); } }