使用新的date时间API格式化date
我正在玩新的date时间API,但运行时:
public class Test { public static void main(String[] args){ String dateFormatted = LocalDate.now() .format(DateTimeFormatter .ofPattern("yyyy-MM-dd HH:mm:ss")); System.out.println(dateFormatted); } }
它抛出:
Exception in thread "main" java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay at java.time.LocalDate.get0(LocalDate.java:680) at java.time.LocalDate.getLong(LocalDate.java:659) at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298) at java.time.format.DateTimeFormatterBuilder$NumberPrinterParser.format(DateTimeFormatterBuilder.java:2543) at java.time.format.DateTimeFormatterBuilder$CompositePrinterParser.format(DateTimeFormatterBuilder.java:2182) at java.time.format.DateTimeFormatter.formatTo(DateTimeFormatter.java:1745) at java.time.format.DateTimeFormatter.format(DateTimeFormatter.java:1719) at java.time.LocalDate.format(LocalDate.java:1685) at Test.main(Test.java:23)
在查看LocalDate类的源代码时,我看到:
private int get0(TemporalField field) { switch ((ChronoField) field) { case DAY_OF_WEEK: return getDayOfWeek().getValue(); case ALIGNED_DAY_OF_WEEK_IN_MONTH: return ((day - 1) % 7) + 1; case ALIGNED_DAY_OF_WEEK_IN_YEAR: return ((getDayOfYear() - 1) % 7) + 1; case DAY_OF_MONTH: return day; case DAY_OF_YEAR: return getDayOfYear(); case EPOCH_DAY: throw new UnsupportedTemporalTypeException("Invalid field 'EpochDay' for get() method, use getLong() instead"); case ALIGNED_WEEK_OF_MONTH: return ((day - 1) / 7) + 1; case ALIGNED_WEEK_OF_YEAR: return ((getDayOfYear() - 1) / 7) + 1; case MONTH_OF_YEAR: return month; case PROLEPTIC_MONTH: throw new UnsupportedTemporalTypeException("Invalid field 'ProlepticMonth' for get() method, use getLong() instead"); case YEAR_OF_ERA: return (year >= 1 ? year : 1 - year); case YEAR: return year; case ERA: return (year >= 1 ? 1 : 0); } throw new UnsupportedTemporalTypeException("Unsupported field: " + field); }
正如文档中所述:
此方法将根据类文档中所述的简单字母和符号模式创build格式化程序。
所有这些字母都被定义了 。
那么为什么DateTimeFormatter.ofPattern
不允许我们使用一些模式字母?
LocalDate
只是一个date,而不是DateTime。 因此,格式化LocalDate
时,“HH:mm:ss”没有意义。 使用LocalDateTime
来代替,假设你想同时代表date和时间。
我想将以下详细信息添加到@James_D的正确答案:
背景:大多数date和时间库(Java java.util.Calendar
,参见.Net-DateTime或JavaScript中的Date
或Perl中的DateTime
)是基于通用的通用唯一时间types的概念德语有诗意的expression“eierlegende Wollmilchsau”)。 在这个devise中不能有一个不受支持的字段。 但价格昂贵:许多时间问题不能用这种不灵活的方法来适当处理,因为很难find各种时间客体的共同点。
JSR-310select了另一种方式 ,即允许不同的时间types,这些时间types由特定types的支持内置字段组成。 自然的结果是,不是每一种types都支持每个可能的领域(用户甚至可以定义自己的专业领域)。 也可以通过编程的方式询问 TemporalAccessor
types的每个对象的特定的一组支持字段。 对于LocalDate
我们发现:
•DAY_OF_WEEK •ALIGNED_DAY_OF_WEEK_IN_MONTH •ALIGNED_DAY_OF_WEEK_IN_YEAR •DAY_OF_MONTH •DAY_OF_YEAR •EPOCH_DAY •ALIGNED_WEEK_OF_MONTH •ALIGNED_WEEK_OF_YEAR •MONTH_OF_YEAR •PROLEPTIC_MONTH •YEAR_OF_ERA •YEAR •ERA
没有解释UnsupportedTemporalTypeException
问题的HOUR_OF_DAY字段。 如果我们看一下模式符号到字段的JSR-310 映射,我们可以看到符号H映射到不支持的HOUR_OF_DAY:
/** Map of letters to fields. */ private static final Map<Character, TemporalField> FIELD_MAP = new HashMap<>(); static { FIELD_MAP.put('G', ChronoField.ERA); FIELD_MAP.put('y', ChronoField.YEAR_OF_ERA); FIELD_MAP.put('u', ChronoField.YEAR); FIELD_MAP.put('Q', IsoFields.QUARTER_OF_YEAR); FIELD_MAP.put('q', IsoFields.QUARTER_OF_YEAR); FIELD_MAP.put('M', ChronoField.MONTH_OF_YEAR); FIELD_MAP.put('L', ChronoField.MONTH_OF_YEAR); FIELD_MAP.put('D', ChronoField.DAY_OF_YEAR); FIELD_MAP.put('d', ChronoField.DAY_OF_MONTH); FIELD_MAP.put('F', ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH); FIELD_MAP.put('E', ChronoField.DAY_OF_WEEK); FIELD_MAP.put('c', ChronoField.DAY_OF_WEEK); FIELD_MAP.put('e', ChronoField.DAY_OF_WEEK); FIELD_MAP.put('a', ChronoField.AMPM_OF_DAY); FIELD_MAP.put('H', ChronoField.HOUR_OF_DAY); FIELD_MAP.put('k', ChronoField.CLOCK_HOUR_OF_DAY); FIELD_MAP.put('K', ChronoField.HOUR_OF_AMPM); FIELD_MAP.put('h', ChronoField.CLOCK_HOUR_OF_AMPM); FIELD_MAP.put('m', ChronoField.MINUTE_OF_HOUR); FIELD_MAP.put('s', ChronoField.SECOND_OF_MINUTE); FIELD_MAP.put('S', ChronoField.NANO_OF_SECOND); FIELD_MAP.put('A', ChronoField.MILLI_OF_DAY); FIELD_MAP.put('n', ChronoField.NANO_OF_SECOND); FIELD_MAP.put('N', ChronoField.NANO_OF_DAY); }
这个字段映射并不意味着该字段被具体types所支持。 parsing发生在几个步骤。 现场映射只是第一步。 第二步是parsing到一个TemporalAccessor
types的原始对象。 最后parsing代表目标types(这里: LocalDate
),并让它决定它是否接受parsing的中间对象中的所有字段值。