@DateTimeFormat与@JsonFormat

简介: @DateTimeFormat与@JsonFormat

一、作用及源码

作用:用于格式化参数,可将date的字符串转化为date对象

/*
 * Copyright 2002-2021 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
 *
 *      https://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.annotation;
 
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
/**
 * Declares that a field or method parameter should be formatted as a date or time.
 *
 * <p>Supports formatting by style pattern, ISO date time pattern, or custom format pattern string.
 * Can be applied to {@link java.util.Date}, {@link java.util.Calendar}, {@link Long} (for
 * millisecond timestamps) as well as JSR-310 {@code java.time} value types.
 *
 * <p>For style-based formatting, set the {@link #style} attribute to the desired style pattern code.
 * The first character of the code is the date style, and the second character is the time style.
 * Specify a character of 'S' for short style, 'M' for medium, 'L' for long, and 'F' for full.
 * The date or time may be omitted by specifying the style character '-' &mdash; for example,
 * 'M-' specifies a medium format for the date with no time.
 *
 * <p>For ISO-based formatting, set the {@link #iso} attribute to the desired {@link ISO} format,
 * such as {@link ISO#DATE}.
 *
 * <p>For custom formatting, set the {@link #pattern} attribute to a date time pattern, such as
 * {@code "yyyy/MM/dd hh:mm:ss a"}.
 *
 * <p>Each attribute is mutually exclusive, so only set one attribute per annotation instance
 * (the one most convenient for your formatting needs).
 *
 * <ul>
 * <li>When the pattern attribute is specified, it takes precedence over both the style and ISO attribute.</li>
 * <li>When the {@link #iso} attribute is specified, it takes precedence over the style attribute.</li>
 * <li>When no annotation attributes are specified, the default format applied is style-based
 * with a style code of 'SS' (short date, short time).</li>
 * </ul>
 *
 * <h3>Time Zones</h3>
 * <p>Whenever the {@link #style} or {@link #pattern} attribute is used, the
 * {@linkplain java.util.TimeZone#getDefault() default time zone} of the JVM will
 * be used when formatting {@link java.util.Date} values. Whenever the {@link #iso}
 * attribute is used when formatting {@link java.util.Date} values, {@code UTC}
 * will be used as the time zone. The same time zone will be applied to any
 * {@linkplain #fallbackPatterns fallback patterns} as well. In order to enforce
 * consistent use of {@code UTC} as the time zone, you can bootstrap the JVM with
 * {@code -Duser.timezone=UTC}.
 *
 * @author Keith Donald
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @since 3.0
 * @see java.time.format.DateTimeFormatter
 * @see org.joda.time.format.DateTimeFormat
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
public @interface DateTimeFormat {
 
  /**
   * The style pattern to use to format the field or method parameter.
   * <p>Defaults to 'SS' for short date, short time. Set this attribute when you
   * wish to format your field or method parameter in accordance with a common
   * style other than the default style.
   * @see #fallbackPatterns
   */
  String style() default "SS";
 
  /**
   * The ISO pattern to use to format the field or method parameter.
   * <p>Supported ISO patterns are defined in the {@link ISO} enum.
   * <p>Defaults to {@link ISO#NONE}, indicating this attribute should be ignored.
   * Set this attribute when you wish to format your field or method parameter
   * in accordance with an ISO format.
   * @see #fallbackPatterns
   */
  ISO iso() default ISO.NONE;
 
  /**
   * The custom pattern to use to format the field or method parameter.
   * <p>Defaults to empty String, indicating no custom pattern String has been
   * specified. Set this attribute when you wish to format your field or method
   * parameter in accordance with a custom date time pattern not represented by
   * a style or ISO format.
   * <p>Note: This pattern follows the original {@link java.text.SimpleDateFormat} style,
   * as also supported by Joda-Time, with strict parsing semantics towards overflows
   * (e.g. rejecting a Feb 29 value for a non-leap-year). As a consequence, 'yy'
   * characters indicate a year in the traditional style, not a "year-of-era" as in the
   * {@link java.time.format.DateTimeFormatter} specification (i.e. 'yy' turns into 'uu'
   * when going through a {@code DateTimeFormatter} with strict resolution mode).
   * @see #fallbackPatterns
   */
  String pattern() default "";
 
  /**
   * The set of custom patterns to use as a fallback in case parsing fails for
   * the primary {@link #pattern}, {@link #iso}, or {@link #style} attribute.
   * <p>For example, if you wish to use the ISO date format for parsing and
   * printing but allow for lenient parsing of user input for various date
   * formats, you could configure something similar to the following.
   * <pre style="code">
   * {@literal @}DateTimeFormat(iso = ISO.DATE, fallbackPatterns = { "M/d/yy", "dd.MM.yyyy" })
   * </pre>
   * <p>Fallback patterns are only used for parsing. They are not used for
   * printing the value as a String. The primary {@link #pattern}, {@link #iso},
   * or {@link #style} attribute is always used for printing. For details on
   * which time zone is used for fallback patterns, see the
   * {@linkplain DateTimeFormat class-level documentation}.
   * <p>Fallback patterns are not supported for Joda-Time value types.
   * @since 5.3.5
   */
  String[] fallbackPatterns() default {};
 
 
  /**
   * Common ISO date time format patterns.
   */
  enum ISO {
 
    /**
     * The most common ISO Date Format {@code yyyy-MM-dd} &mdash; for example,
     * "2000-10-31".
     */
    DATE,
 
    /**
     * The most common ISO Time Format {@code HH:mm:ss.SSSXXX} &mdash; for example,
     * "01:30:00.000-05:00".
     */
    TIME,
 
    /**
     * The most common ISO Date Time Format {@code yyyy-MM-dd'T'HH:mm:ss.SSSXXX}
     * &mdash; for example, "2000-10-31T01:30:00.000-05:00".
     */
    DATE_TIME,
 
    /**
     * Indicates that no ISO-based format pattern should be applied.
     */
    NONE
  }
 
}
package com.fasterxml.jackson.annotation;
 
import java.lang.annotation.*;
import java.util.Locale;
import java.util.TimeZone;
 
/**
 * General-purpose annotation used for configuring details of how
 * values of properties are to be serialized.
 * Unlike most other Jackson annotations, annotation does not
 * have specific universal interpretation: instead, effect depends on datatype
 * of property being annotated (or more specifically, deserializer
 * and serializer being used).
 *<p>
 * Common uses include choosing between alternate representations -- for example,
 * whether {@link java.util.Date} is to be serialized as number (Java timestamp)
 * or String (such as ISO-8601 compatible time value) -- as well as configuring
 * exact details with {@link #pattern} property.
 *<p>
 * As of Jackson 2.6, known special handling includes:
 *<ul>
 * <li>{@link java.util.Date}: Shape can  be {@link Shape#STRING} or {@link Shape#NUMBER};
 *    pattern may contain {@link java.text.SimpleDateFormat}-compatible pattern definition.
 *   </li>
 * <li>Can be used on Classes (types) as well, for modified default behavior, possibly
 *   overridden by per-property annotation
 *   </li>
 * <li>{@link java.lang.Enum}s: Shapes {@link Shape#STRING} and {@link Shape#NUMBER} can be
 *    used to change between numeric (index) and textual (name or <code>toString()</code>);
 *    but it is also possible to use {@link Shape#OBJECT} to serialize (but not deserialize)
 *    {@link java.lang.Enum}s as JSON Objects (as if they were POJOs). NOTE: serialization
 *     as JSON Object only works with class annotation; 
 *    will not work as per-property annotation.
 *   </li>
 * <li>{@link java.util.Collection}s can be serialized as (and deserialized from) JSON Objects,
 *    if {@link Shape#OBJECT} is used. NOTE: can ONLY be used as class annotation;
 *    will not work as per-property annotation.
 *   </li>
 * <li>{@link java.lang.Number} subclasses can be serialized as full objects if
 *    {@link Shape#OBJECT} is used. Otherwise the default behavior of serializing to a
 *    scalar number value will be preferred. NOTE: can ONLY be used as class annotation;
 *    will not work as per-property annotation.
 *   </li>
 *</ul>
 *
 * @since 2.0
 */
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonFormat
{
    /**
     * Value that indicates that default {@link java.util.Locale}
     * (from deserialization or serialization context) should be used:
     * annotation does not define value to use.
     */
    public final static String DEFAULT_LOCALE = "##default";
 
    /**
     * Value that indicates that default {@link java.util.TimeZone}
     * (from deserialization or serialization context) should be used:
     * annotation does not define value to use.
     *<p>
     * NOTE: default here does NOT mean JVM defaults but Jackson databindings
     * default, usually UTC, but may be changed on <code>ObjectMapper</code>.
     */
    public final static String DEFAULT_TIMEZONE = "##default";
    
    /**
     * Datatype-specific additional piece of configuration that may be used
     * to further refine formatting aspects. This may, for example, determine
     * low-level format String used for {@link java.util.Date} serialization;
     * however, exact use is determined by specific <code>JsonSerializer</code>
     */
    public String pattern() default "";
 
    /**
     * Structure to use for serialization: definition of mapping depends on datatype,
     * but usually has straight-forward counterpart in data format (JSON).
     * Note that commonly only a subset of shapes is available; and if 'invalid' value
     * is chosen, defaults are usually used.
     */
    public Shape shape() default Shape.ANY;
 
    /**
     * {@link java.util.Locale} to use for serialization (if needed).
     * Special value of {@link #DEFAULT_LOCALE}
     * can be used to mean "just use the default", where default is specified
     * by the serialization context, which in turn defaults to system
     * defaults ({@link java.util.Locale#getDefault()}) unless explicitly
     * set to another locale.
     */
    public String locale() default DEFAULT_LOCALE;
    
    /**
     * {@link java.util.TimeZone} to use for serialization (if needed).
     * Special value of {@link #DEFAULT_TIMEZONE}
     * can be used to mean "just use the default", where default is specified
     * by the serialization context, which in turn defaults to system
     * default (UTC) unless explicitly set to another timezone.
     */
    public String timezone() default DEFAULT_TIMEZONE;
 
    /**
     * Property that indicates whether "lenient" handling should be enabled or
     * disabled. This is relevant mostly for deserialization of some textual
     * datatypes, especially date/time types.
     *<p>
     * Note that underlying default setting depends on datatype (or more precisely
     * deserializer for it): for most date/time types, default is for leniency
     * to be enabled.
     * 
     * @since 2.9
     */
    public OptBoolean lenient() default OptBoolean.DEFAULT;
 
    /**
     * Set of {@link JsonFormat.Feature}s to explicitly enable with respect
     * to handling of annotated property. This will have precedence over possible
     * global configuration.
     *
     * @since 2.6
     */
    public JsonFormat.Feature[] with() default { };
 
    /**
     * Set of {@link JsonFormat.Feature}s to explicitly disable with respect
     * to handling of annotated property. This will have precedence over possible
     * global configuration.
     *
     * @since 2.6
     */
    public JsonFormat.Feature[] without() default { };
 
    /*
    /**********************************************************
    /* Value enumeration(s), value class(es)
    /**********************************************************
     */
 
    /**
     * Value enumeration used for indicating preferred Shape; translates
     * loosely to JSON types, with some extra values to indicate less precise
     * choices (i.e. allowing one of multiple actual shapes)
     */
    public enum Shape
    {
        /**
         * Marker enum value that indicates "whatever" choice, meaning that annotation
         * does NOT specify shape to use.
         * Note that this is different from {@link Shape#NATURAL}, which
         * specifically instructs use of the "natural" shape for datatype.
         */
        ANY,
 
        /**
         * Marker enum value that indicates the "default" choice for given datatype;
         * for example, JSON String for {@link java.lang.String}, or JSON Number
         * for Java numbers.
         * Note that this is different from {@link Shape#ANY} in that this is actual
         * explicit choice that overrides possible default settings.
         *
         * @since 2.8
         */
        NATURAL,
        
        /**
         * Value that indicates shape should not be structural (that is, not
         * {@link #ARRAY} or {@link #OBJECT}, but can be any other shape.
         */
        SCALAR,
 
        /**
         * Value that indicates that (JSON) Array type should be used.
         */
        ARRAY,
        
        /**
         * Value that indicates that (JSON) Object type should be used.
         */
        OBJECT,
 
        /**
         * Value that indicates that a numeric (JSON) type should be used
         * (but does not specify whether integer or floating-point representation
         * should be used)
         */
        NUMBER,
 
        /**
         * Value that indicates that floating-point numeric type should be used
         */
        NUMBER_FLOAT,
 
        /**
         * Value that indicates that integer number type should be used
         * (and not {@link #NUMBER_FLOAT}).
         */
        NUMBER_INT,
 
        /**
         * Value that indicates that (JSON) String type should be used.
         */
        STRING,
        
        /**
         * Value that indicates that (JSON) boolean type
         * (true, false) should be used.
         */
        BOOLEAN,
 
        /**
         * Value that indicates that Binary type (native, if format supports it;
         * encoding using Base64 if only textual types supported) should be used.
         *
         * @since 2.10
         */
        BINARY
        ;
 
        public boolean isNumeric() {
            return (this == NUMBER) || (this == NUMBER_INT) || (this == NUMBER_FLOAT);
        }
 
        public boolean isStructured() {
            return (this == OBJECT) || (this == ARRAY);
        }
    }
 
    /**
     * Set of features that can be enabled/disabled for property annotated.
     * These often relate to specific <code>SerializationFeature</code>
     * or <code>DeserializationFeature</code>, as noted by entries.
     *<p>
     * Note that whether specific setting has an effect depends on whether
     * <code>JsonSerializer</code> / <code>JsonDeserializer</code> being used
     * takes the format setting into account. If not, please file an issue
     * for adding support via issue tracker for package that has handlers
     * (if you know which one; if not, just use `jackson-databind`).
     *
     * @since 2.6
     */
    public enum Feature {
        /**
         * Override for <code>DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY</code>
         * which will allow deserialization of JSON non-array values into single-element
         * Java arrays and {@link java.util.Collection}s.
         */
        ACCEPT_SINGLE_VALUE_AS_ARRAY,
 
        /**
         * Override for <code>MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES</code>,
         * which allows case-insensitive matching of property names (but NOT values,
         * see {@link #ACCEPT_CASE_INSENSITIVE_VALUES} for that).
         *<p>
         * Only affects deserialization, has no effect on serialization.
         * 
         * @since 2.8
         */
        ACCEPT_CASE_INSENSITIVE_PROPERTIES,
 
        /**
         * Override for <code>MapperFeature.ACCEPT_CASE_INSENSITIVE_VALUES</code>,
         * which allows case-sensitive matching of (some) property values, such
         * as {@code Enum}s.
         * Only affects deserialization, has no effect on serialization.
         * 
         * @since 2.10
         */
        ACCEPT_CASE_INSENSITIVE_VALUES,
 
        /**
         * Override for <code>SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS</code>,
         * similar constraints apply.
         */
        WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS,
 
        /**
         * Override for <code>SerializationFeature.WRITE_DATES_WITH_ZONE_ID</code>,
         * similar constraints apply.
         */
        WRITE_DATES_WITH_ZONE_ID,
 
        /**
         * Override for <code>SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED</code>
         * which will force serialization of single-element arrays and {@link java.util.Collection}s
         * as that single element and excluding array wrapper.
         */
        WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED,
 
        /**
         * Override for <code>SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS</code>,
         * enabling of which will force sorting of {@link java.util.Map} keys before
         * serialization.
         */
        WRITE_SORTED_MAP_ENTRIES,
 
        /**
         * Override for <code>DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIMEZONE</code>
         * that specifies whether context provided timezone
         * <code>DeserializationContext.getTimeZone()</code> should be used to adjust Date/Time
         * values on deserialization, even if value itself contains timezone informatio
         *<p>
         * NOTE: due to limitations of "old" JDK date/time types (that is,
         * {@link java.util.Date} and {@link java.util.Calendar}), this setting is only
         * applicable to <code>Joda</code> and <code>Java 8 date/time</code> values,
         * but not to <code>java.util.Date</code> or <code>java.util.Calendar</code>.
         *
         * @since 2.8
         */
        ADJUST_DATES_TO_CONTEXT_TIME_ZONE
    }
 
    /**
     * Helper class that encapsulates information equivalent to {@link java.lang.Boolean}
     * valued {@link java.util.EnumMap}.
     *
     * @since 2.6
     */
    public static class Features
    {
        private final int _enabled, _disabled;
 
        private final static Features EMPTY = new Features(0, 0);
        
        private Features(int e, int d) {
            _enabled = e;
            _disabled = d;
        }
 
        public static Features empty() {
            return EMPTY;
        }
        
        public static Features construct(JsonFormat f) {
            return construct(f.with(), f.without());
        }
        
        public static Features construct(Feature[] enabled, Feature[] disabled)
        {
            int e = 0;
            for (Feature f : enabled) {
                e |= (1 << f.ordinal());
            }
            int d = 0;
            for (Feature f : disabled) {
                d |= (1 << f.ordinal());
            }
            return new Features(e, d);
        }
 
        public Features withOverrides(Features overrides) {
            // Cheap checks first: maybe one is empty?
            if (overrides == null) {
                return this;
            }
            int overrideD = overrides._disabled;
            int overrideE = overrides._enabled;
            if ((overrideD == 0) && (overrideE == 0)) {
                return this;
            }
            if ((_enabled == 0) && (_disabled == 0)) {
                return overrides;
            }
            // If not, calculate combination with overrides
            int newE = (_enabled & ~overrideD) | overrideE;
            int newD = (_disabled & ~overrideE) | overrideD;
            
            // one more thing; no point in creating new instance if there's no change
            if ((newE == _enabled) && (newD == _disabled)) {
                return this;
            }
            
            return new Features(newE, newD);
        }
 
        public Features with(Feature...features) {
            int e = _enabled;
            for (Feature f : features) {
                e |= (1 << f.ordinal());
            }
            return (e == _enabled) ? this : new Features(e, _disabled);
        }
 
        public Features without(Feature...features) {
            int d = _disabled;
            for (Feature f : features) {
                d |= (1 << f.ordinal());
            }
            return (d == _disabled) ? this : new Features(_enabled, d);
        }
 
        public Boolean get(Feature f) {
            int mask = (1 << f.ordinal());
            if ((_disabled & mask) != 0) {
                return Boolean.FALSE;
            }
            if ((_enabled & mask) != 0) {
                return Boolean.TRUE;
            }
            return null;
        }
 
        @Override
        public String toString() {
            if (this == EMPTY) {
                return "EMPTY";
            }
            return String.format("(enabled=0x%x,disabled=0x%x)", _enabled, _disabled);
        }
 
        @Override
        public int hashCode() {
            return _disabled + _enabled;
        }
 
        @Override
        public boolean equals(Object o) {
            if (o == this) return true;
            if (o == null) return false;
            if (o.getClass() != getClass()) return false;
            Features other = (Features) o;
            return (other._enabled == _enabled) && (other._disabled == _disabled);
        }
    }
 
    /**
     * Helper class used to contain information from a single {@link JsonFormat}
     * annotation.
     */
    public static class Value
        implements JacksonAnnotationValue<JsonFormat>, // since 2.6
            java.io.Serializable
    {
        private static final long serialVersionUID = 1L;
 
        private final static Value EMPTY = new Value();
 
        private final String _pattern;
        private final Shape _shape;
        private final Locale _locale;
 
        private final String _timezoneStr;
 
        /**
         * @since 2.9
         */
        private final Boolean _lenient;
 
        /**
         * @since 2.6
         */
        private final Features _features;
 
        // lazily constructed when created from annotations
        private transient TimeZone _timezone;
        
        public Value() {
            this("", Shape.ANY, "", "", Features.empty(), null);
        }
        
        public Value(JsonFormat ann) {
            this(ann.pattern(), ann.shape(), ann.locale(), ann.timezone(),
                    Features.construct(ann), ann.lenient().asBoolean());
        }
 
        /**
         * @since 2.9
         */
        public Value(String p, Shape sh, String localeStr, String tzStr, Features f,
                Boolean lenient)
        {
            this(p, sh,
                    (localeStr == null || localeStr.length() == 0 || DEFAULT_LOCALE.equals(localeStr)) ?
                            null : new Locale(localeStr),
                    (tzStr == null || tzStr.length() == 0 || DEFAULT_TIMEZONE.equals(tzStr)) ?
                            null : tzStr,
                    null, f, lenient);
        }
 
        /**
         * @since 2.9
         */
        public Value(String p, Shape sh, Locale l, TimeZone tz, Features f,
                Boolean lenient)
        {
            _pattern = (p == null) ? "" : p;
            _shape = (sh == null) ? Shape.ANY : sh;
            _locale = l;
            _timezone = tz;
            _timezoneStr = null;
            _features = (f == null) ? Features.empty() : f;
            _lenient = lenient;
        }
 
        /**
         * @since 2.9
         */
        public Value(String p, Shape sh, Locale l, String tzStr, TimeZone tz, Features f,
                Boolean lenient)
        {
            _pattern = (p == null) ? "" : p;
            _shape = (sh == null) ? Shape.ANY : sh;
            _locale = l;
            _timezone = tz;
            _timezoneStr = tzStr;
            _features = (f == null) ? Features.empty() : f;
            _lenient = lenient;
        }
 
        @Deprecated // since 2.9
        public Value(String p, Shape sh, Locale l, String tzStr, TimeZone tz, Features f) {
            this(p, sh, l, tzStr, tz, f, null);
        }
        
        @Deprecated // since 2.9
        public Value(String p, Shape sh, String localeStr, String tzStr, Features f) {
            this(p, sh, localeStr, tzStr, f, null);
        }
        @Deprecated // since 2.9
        public Value(String p, Shape sh, Locale l, TimeZone tz, Features f) {
            this(p, sh, l, tz, f, null);
        }
        
        /**
         * @since 2.7
         */
        public final static Value empty() {
            return EMPTY;
        }
 
        /**
         * Helper method that will try to combine values from two {@link Value}
         * instances, using one as base settings, and the other as overrides
         * to use instead of base values when defined; base values are only
         * use if override does not specify a value (matching value is null
         * or logically missing).
         * Note that one or both of value instances may be `null`, directly;
         * if both are `null`, result will also be `null`; otherwise never null.
         *
         * @since 2.8
         */
        public static Value merge(Value base, Value overrides)
        {
            return (base == null) ? overrides
                    : base.withOverrides(overrides);
        }
 
        /**
         * @since 2.8
         */
        public static Value mergeAll(Value... values)
        {
            Value result = null;
            for (Value curr : values) {
                if (curr != null) {
                    result = (result == null)  ? curr : result.withOverrides(curr);
                }
            }
            return result;
        }
 
        /**
         * @since 2.7
         */
        public final static Value from(JsonFormat ann) {
            return (ann == null) ? EMPTY : new Value(ann);
        }
 
        /**
         * @since 2.7
         */
        public final Value withOverrides(Value overrides) {
            if ((overrides == null) || (overrides == EMPTY) || (overrides == this)) {
                return this;
            }
            if (this == EMPTY) { // cheesy, but probably common enough
                return overrides;
            }
            String p = overrides._pattern;
            if ((p == null) || p.isEmpty()) {
                p = _pattern;
            }
            Shape sh = overrides._shape;
            if (sh == Shape.ANY) {
                sh = _shape;
            }
            Locale l = overrides._locale;
            if (l == null) {
                l = _locale;
            }
            Features f = _features;
            if (f == null) {
                f = overrides._features;
            } else {
                f = f.withOverrides(overrides._features);
            }
            Boolean lenient = overrides._lenient;
            if (lenient == null) {
                lenient = _lenient;
            }
 
            // timezone not merged, just choose one
            String tzStr = overrides._timezoneStr;
            TimeZone tz;
            
            if ((tzStr == null) || tzStr.isEmpty()) { // no overrides, use space
                tzStr = _timezoneStr;
                tz = _timezone;
            } else {
                tz = overrides._timezone;
            }
            return new Value(p, sh, l, tzStr, tz, f, lenient);
        }
 
        /**
         * @since 2.6
         */
        public static Value forPattern(String p) {
            return new Value(p, null, null, null, null, Features.empty(), null);
        }
 
        /**
         * @since 2.7
         */
        public static Value forShape(Shape sh) {
            return new Value("", sh, null, null, null, Features.empty(), null);
        }
 
        /**
         * @since 2.9
         */
        public static Value forLeniency(boolean lenient) {
            return new Value("", null, null, null, null, Features.empty(),
                    Boolean.valueOf(lenient));
        }
 
        /**
         * @since 2.1
         */
        public Value withPattern(String p) {
            return new Value(p, _shape, _locale, _timezoneStr, _timezone,
                    _features, _lenient);
        }
 
        /**
         * @since 2.1
         */
        public Value withShape(Shape s) {
            if (s == _shape) {
                return this;
            }
            return new Value(_pattern, s, _locale, _timezoneStr, _timezone,
                    _features, _lenient);
        }
 
        /**
         * @since 2.1
         */
        public Value withLocale(Locale l) {
            return new Value(_pattern, _shape, l, _timezoneStr, _timezone,
                    _features, _lenient);
        }
 
        /**
         * @since 2.1
         */
        public Value withTimeZone(TimeZone tz) {
            return new Value(_pattern, _shape, _locale, null, tz,
                    _features, _lenient);
        }
 
        /**
         * @since 2.9
         */
        public Value withLenient(Boolean lenient) {
            if (lenient == _lenient) {
                return this;
            }
            return new Value(_pattern, _shape, _locale, _timezoneStr, _timezone,
                    _features, lenient);
        }
 
        /**
         * @since 2.6
         */
        public Value withFeature(JsonFormat.Feature f) {
            Features newFeats = _features.with(f);
            return (newFeats == _features) ? this :
                new Value(_pattern, _shape, _locale, _timezoneStr, _timezone,
                        newFeats, _lenient);
        }
 
        /**
         * @since 2.6
         */
        public Value withoutFeature(JsonFormat.Feature f) {
            Features newFeats = _features.without(f);
            return (newFeats == _features) ? this :
                new Value(_pattern, _shape, _locale, _timezoneStr, _timezone,
                        newFeats, _lenient);
        }
 
        @Override
        public Class<JsonFormat> valueFor() {
            return JsonFormat.class;
        }
        
        public String getPattern() { return _pattern; }
        public Shape getShape() { return _shape; }
        public Locale getLocale() { return _locale; }
 
        /**
         * @return {@code Boolean.TRUE} if explicitly set to true; {@code Boolean.FALSE}
         *   if explicit set to false; or {@code null} if not set either way (assuming
         *   "default leniency" for the context)
         *
         * @since 2.9
         */
        public Boolean getLenient() {
            return _lenient;
        }
 
        /**
         * Convenience method equivalent to
         *<pre>
         *   Boolean.TRUE.equals(getLenient())
         *</pre>
         * that is, returns {@code true} if (and only if) leniency has been explicitly
         * set to {code true}; but not if it is undefined.
         *
         * @since 2.9
         */
        public boolean isLenient() {
            return Boolean.TRUE.equals(_lenient);
        }
 
        /**
         * Alternate access (compared to {@link #getTimeZone()}) which is useful
         * when caller just wants time zone id to convert, but not as JDK
         * provided {@link TimeZone}
         * 
         * @since 2.4
         */
        public String timeZoneAsString() {
            if (_timezone != null) {
                return _timezone.getID();
            }
            return _timezoneStr;
        }
        
        public TimeZone getTimeZone() {
            TimeZone tz = _timezone;
            if (tz == null) {
                if (_timezoneStr == null) {
                    return null;
                }
                tz = TimeZone.getTimeZone(_timezoneStr);
                _timezone = tz;
            }
            return tz;
        }
 
        /**
         * @since 2.4
         */
        public boolean hasShape() { return _shape != Shape.ANY; }
        
        /**
         * @since 2.4
         */
        public boolean hasPattern() {
            return (_pattern != null) && (_pattern.length() > 0);
        }
        
        /**
         * @since 2.4
         */
        public boolean hasLocale() { return _locale != null; }
 
        /**
         * @since 2.4
         */
        public boolean hasTimeZone() {
            return (_timezone != null) || (_timezoneStr != null && !_timezoneStr.isEmpty());
        }
 
        /**
         * Accessor for checking whether there is a setting for leniency.
         * NOTE: does NOT mean that `lenient` is `true` necessarily; just that
         * it has been set.
         *
         * @since 2.9
         */
        public boolean hasLenient() {
            return _lenient != null;
        }
 
        /**
         * Accessor for checking whether this format value has specific setting for
         * given feature. Result is 3-valued with either `null`, {@link Boolean#TRUE} or
         * {@link Boolean#FALSE}, indicating 'yes/no/dunno' choices, where `null` ("dunno")
         * indicates that the default handling should be used based on global defaults,
         * and there is no format override.
         *
         * @since 2.6
         */
        public Boolean getFeature(JsonFormat.Feature f) {
            return _features.get(f);
        }
 
        /**
         * Accessor for getting full set of features enabled/disabled.
         *
         * @since 2.8
         */
        public Features getFeatures() {
            return _features;
        }
 
        @Override
        public String toString() {
            return String.format("JsonFormat.Value(pattern=%s,shape=%s,lenient=%s,locale=%s,timezone=%s,features=%s)",
                    _pattern, _shape, _lenient, _locale, _timezoneStr, _features);
        }
 
        @Override
        public int hashCode() {
             int hash = (_timezoneStr == null) ? 1 : _timezoneStr.hashCode();
             if (_pattern != null) {
                 hash ^= _pattern.hashCode();
             }
             hash += _shape.hashCode();
             if (_lenient != null) {
                 hash ^= _lenient.hashCode();
             }
             if (_locale != null) {
                 hash += _locale.hashCode();
             }
             hash ^= _features.hashCode();
             return hash;
        }
 
        @Override
        public boolean equals(Object o) {
            if (o == this) return true;
            if (o == null) return false;
            if (o.getClass() != getClass()) return false;
            Value other = (Value) o;
 
            if ((_shape != other._shape) 
                    || !_features.equals(other._features)) {
                return false;
            }
            return _equal(_lenient, other._lenient)
                    && _equal(_timezoneStr, other._timezoneStr)
                    && _equal(_pattern, other._pattern)
                    && _equal(_timezone, other._timezone)
                    && _equal(_locale, other._locale);
        }
 
        private static <T> boolean _equal(T value1, T value2)
        {
            if (value1 == null) {
                return (value2 == null);
            }
            if (value2 == null) {
                return false;
            }
            return value1.equals(value2);
        }
    }
}

二、使用的位置

@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonFormat
{
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
public @interface DateTimeFormat {
}
@Target用来表示注解作用范围,超过这个作用范围,编译的时候就会报错。
 
@Target:注解的作用目标
 
@Target(ElementType.TYPE)——接口、类、枚举、注解
@Target(ElementType.FIELD)——字段、枚举的常量
@Target(ElementType.METHOD)——方法
@Target(ElementType.PARAMETER)——方法参数
@Target(ElementType.CONSTRUCTOR) ——构造函数
@Target(ElementType.LOCAL_VARIABLE)——局部变量
@Target(ElementType.ANNOTATION_TYPE)——注解
@Target(ElementType.PACKAGE)——包,用于记录java文件的package信息

二者均可用于字段、方法、方法参数、注解上;

三、作用于字段

1、获取日期

import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
 
import java.util.Date;
 
@Data
public class DateTimeFormatTest {
 
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthDay;
}


import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
 
import java.util.Date;
 
@Data
public class JsonFormatTest {
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date birthDay;
}
    @GetMapping("/getJsonFormatTest")
    public JsonFormatTest getJsonFormatTest() {
        JsonFormatTest jsonFormatTest = new JsonFormatTest();
        jsonFormatTest.setBirthDay(new Date());
        return jsonFormatTest;
    }
 
    @GetMapping("/getDateTimeFormatTest")
    public DateTimeFormatTest getDateTimeFormatTest() {
        DateTimeFormatTest dateTimeFormatTest = new DateTimeFormatTest();
        dateTimeFormatTest.setBirthDay(new Date());
        return dateTimeFormatTest;
    }

@JsonFormat序列化可以用

2、提交数据

1  @PostMapping("/setJsonFormatTest")
    public void setJsonFormatTest(@RequestBody JsonFormatTest jsonFormatTest) {
        System.out.println("jsonFormatTest"+JSON.toJSONString(jsonFormatTest));
    }
 
    @PostMapping("/setDateTimeFormatTest")
    public void setDateTimeFormatTest(@RequestBody DateTimeFormatTest dateTimeFormatTest) {
        System.out.println("dateTimeFormatTest"+JSON.toJSONString(dateTimeFormatTest));
    }

dateTimeFormatTest{"birthDay":1685923200000}

jsonFormatTest{"birthDay":1685923200000}

二者都可以使用;

四、作用于参数

    @GetMapping("/getJsonFormatParamTest")
    public Date getJsonFormatParamTest(@JsonFormat(pattern = "yyyy-MM-dd") Date birthDay) {
        return birthDay;
    }
 
    @GetMapping("/getDateTimeFormatParamTest")
    public Date getDateTimeFormatParamTest(@DateTimeFormat(pattern = "yyyy-MM-dd") Date birthDay) {
        return birthDay;
    }


@DateTimeFormat可以使用;

五、总结

@DateTimeFormat:字符串转换为时间(字段、方法参数);

@JsonFormat:字符串转换为时间(字段)  时间转换为字符串;

推荐:字符串转时间使用@DateTimeFormat(提交数据),时间转字符串使用@JsonFormat(返回数据)。

相关文章
|
Java 测试技术 数据库
JsonFormat遇到夏时制
前段时间遇到一个离谱的问题,用户的生日通过后端返回直接少了一天,debug半天发现每次在调试时数据库返回都是正确的值,但是接口响应体中莫名奇妙,减少一天。
520 0
|
4月前
Jackson 使用 @JsonFormat 注解进行时间格式化
Jackson 使用 @JsonFormat 注解进行时间格式化
490 2
|
4月前
|
存储 安全 Java
使用SimpleDateFormat进行日期格式化
使用SimpleDateFormat进行日期格式化
|
前端开发
【注解使用】@JsonFormat与@DateTimeFormat注解的使用
【注解使用】@JsonFormat与@DateTimeFormat注解的使用
191 0
|
Java
LocalDateTime序列化成yyyy-MM-dd HH:mm
LocalDateTime序列化成yyyy-MM-dd HH:mm
243 0
使用 SimpleDateFormat 格式化日期
使用 SimpleDateFormat 格式化日期
133 0
SimpleDateFormat的pattern
SimpleDateFormat的pattern
有关日期格式化的问题
有关日期格式化的问题
104 0
|
前端开发 Java Spring
【小家Spring】@DateTimeFormat和@NumberFormat以及@JsonFormat的使用
【小家Spring】@DateTimeFormat和@NumberFormat以及@JsonFormat的使用
【小家Spring】@DateTimeFormat和@NumberFormat以及@JsonFormat的使用
|
存储 前端开发 Java
@JsonFormat与@DateTimeFormat的区别
@JsonFormat与@DateTimeFormat的区别
364 0