频道
bg

使用@DateTimeFormat注解转换ZondDateTime

coding四月 18, 20161mins
SpringFramework

概述H2

JSR310的基于@DateTimeFormat注解的转换器实在DefaultFormattingConversionService中注册的

bash

public static void addDefaultFormatters(FormatterRegistry formatterRegistry) {
...
if (jsr310Present) {
// just handling JSR-310 specific date and time types
new DateTimeFormatterRegistrar().registerFormatters(formatterRegistry);
}
}

bash

DateTimeFormatterRegistrar#registerFormatters {
...
registry.addFormatterForFieldAnnotation(new Jsr310DateTimeFormatAnnotationFormatterFactory());
}

分析H2

但是使用@DateTimeFormat并不能说随意的指定格式,这依赖这注解的字段类型,比如说如果像下面这样使用是不行的

bash

@DateTimeFormat(pattern = "yyyy年MM月dd日")
private ZonedDateTime endTime;

因为ZonedDateTime转换的时候还需要时间以及时区信息 下面是获取Parse的逻辑

bash

AnnotationParserConverter#convert
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
Annotation ann = targetType.getAnnotation(this.annotationType);
if (ann == null) {
throw new IllegalStateException(
"Expected [" + this.annotationType.getName() + "] to be present on " + targetType);
}
AnnotationConverterKey converterKey = new AnnotationConverterKey(ann, targetType.getObjectType());
GenericConverter converter = cachedParsers.get(converterKey);
if (converter == null) {
Parser<?> parser = this.annotationFormatterFactory.getParser(
converterKey.getAnnotation(), converterKey.getFieldType());
converter = new ParserConverter(this.fieldType, parser, FormattingConversionService.this);
cachedParsers.put(converterKey, converter);
}
return converter.convert(source, sourceType, targetType);
}
Jsr310DateTimeFormatAnnotationFormatterFactory#getParser
public Parser<?> getParser(DateTimeFormat annotation, Class<?> fieldType) {
DateTimeFormatter formatter = getFormatter(annotation, fieldType);
return new TemporalAccessorParser((Class<? extends TemporalAccessor>) fieldType, formatter);
}
/**
* Factory method used to create a {@link DateTimeFormatter}.
* @param annotation the format annotation for the field
* @param fieldType the declared type of the field
* @return a {@link DateTimeFormatter} instance
*/
protected DateTimeFormatter getFormatter(DateTimeFormat annotation, Class<?> fieldType) {
DateTimeFormatterFactory factory = new DateTimeFormatterFactory();
factory.setStylePattern(resolveEmbeddedValue(annotation.style()));
factory.setIso(annotation.iso());
factory.setPattern(resolveEmbeddedValue(annotation.pattern()));
return factory.createDateTimeFormatter();
}

下面是转换的逻辑

bash

ZonedDateTime#parse
public static ZonedDateTime parse(CharSequence text, DateTimeFormatter formatter) {
Objects.requireNonNull(formatter, "formatter");
return formatter.parse(text, ZonedDateTime::from);
}
DateTimeFormatter#parse
public <T> T parse(CharSequence text, TemporalQuery<T> query) {
Objects.requireNonNull(text, "text");
Objects.requireNonNull(query, "query");
try {
return parseResolved0(text, null).query(query);
} catch (DateTimeParseException ex) {
throw ex;
} catch (RuntimeException ex) {
throw createError(text, ex);
}
}
ZonedDateTime#from
public static ZonedDateTime from(TemporalAccessor temporal) {
if (temporal instanceof ZonedDateTime) {
return (ZonedDateTime) temporal;
}
try {
ZoneId zone = ZoneId.from(temporal);
if (temporal.isSupported(INSTANT_SECONDS)) {
long epochSecond = temporal.getLong(INSTANT_SECONDS);
int nanoOfSecond = temporal.get(NANO_OF_SECOND);
return create(epochSecond, nanoOfSecond, zone);
} else {
LocalDate date = LocalDate.from(temporal);
LocalTime time = LocalTime.from(temporal);
return of(date, time, zone);
}
} catch (DateTimeException ex) {
throw new DateTimeException("Unable to obtain ZonedDateTime from TemporalAccessor: " +
temporal + " of type " + temporal.getClass().getName(), ex);
}
}

如果没有时区信息ZoneId zone = ZoneId.from(temporal)会报错,如果没有时间信息LocalTime time = LocalTime.from(temporal);会报错

结论H2

所以上讲,@DateTimeFormat配合ZonedDateTime使用,指定的格式至少是包含时区和时间信息的,例如yyyy-MM-dd HH:mm:ss.SSSZ

评论


新的评论

匹配您的Gravatar头像

Joen Yu

@2022 JoenYu, all rights reserved. Made with love.