为什么这个测试通过,而月份值显然是无效的(13)?
@Test
public void test() {
String format = "uuuuMM";
String value = "201713";
DateTimeFormatter.ofPattern(format).withResolverStyle(ResolverStyle.STRICT)
.parse(value);
}
使用时态查询时,会抛出预期的DateTimeParseException
:
DateTimeFormatter.ofPattern(format).withResolverStyle(ResolverStyle.STRICT)
.parse(value, YearMonth::from);
未指定TemporalQuery
时会发生什么?
编辑:13值似乎是一个特殊的值,因为我学到了ΦXocę웃Пepeúpaツ的答案(见Undecimber)。
但即使使用其他值(例如50:
)也不会引发异常@Test
public void test() {
String format = "uuuuMM";
String value = "201750";
DateTimeFormatter.ofPattern(format).withResolverStyle(ResolverStyle.STRICT)
.parse(value);
}
答案 0 :(得分:5)
我在这里进行了一些调试,发现解析过程的一部分是根据格式化程序的年表来检查字段。
当您创建DateTimeFormatter
,by default时,它会使用IsoChronology
,用于解析日期字段。在此解析阶段,调用the method java.time.chrono.AbstractChronology::resolveDate
。
如果查看source,您会看到以下逻辑:
if (fieldValues.containsKey(YEAR)) {
if (fieldValues.containsKey(MONTH_OF_YEAR)) {
if (fieldValues.containsKey(DAY_OF_MONTH)) {
return resolveYMD(fieldValues, resolverStyle);
}
....
return null;
由于输入只包含年和月字段,fieldValues.containsKey(DAY_OF_MONTH)
返回false
,因此该方法返回null
而不是您可以在Parsed
class中看到其他检查。
因此,在没有201750
的情况下解析201713
或TemporalQuery
时,由于上述逻辑,不会进行额外检查,并且parse
方法返回{{1通过以下代码可以看到对象:
java.time.format.Parsed
输出结果为:
class java.time.format.Parsed
{Year = 2017,MonthOfYear = 50},ISO
请注意,返回的对象的类型为DateTimeFormatter fmt = DateTimeFormatter.ofPattern("uuuuMM").withResolverStyle(ResolverStyle.STRICT);
TemporalAccessor parsed = fmt.parse("201750");
System.out.println(parsed.getClass());
System.out.println(parsed);
,并且打印它会显示已解析的字段(年份和月份)。
当您使用java.time.format.Parsed
调用parse
时,TemporalQuery
对象会传递给查询并且其字段已经过验证(当然这取决于查询,但API内置的总是验证)。
如果是Parsed
,它会使用相应的YearMonth::from
(MONTH_OF_YEAR和YEAR)检查年份和月份是否有效,并且月份字段接受仅限值from 1 to 12。
这就是为什么只调用ChronoField
不会抛出异常,而是使用parse(value)
调用。
只是在所有日期字段(年,月和日)出现时检查上面的逻辑:
TemporalQuery
这引发:
线程“main”中的异常java.time.format.DateTimeParseException:无法解析文本“20175010”: MonthOfYear的值无效(有效值1 - 12):50
由于所有日期字段都存在,DateTimeFormatter fmt = DateTimeFormatter.ofPattern("uuuuMMdd").withResolverStyle(ResolverStyle.STRICT);
fmt.parse("20175010");
会返回fieldValues.containsKey(DAY_OF_MONTH)
,现在它会检查它是否为有效日期(使用resolveYMD
method)。
答案 1 :(得分:3)
第13个月被称为:Undecimber
我们许多人使用的 gregorian 日历仅允许 12 月,但java包含对日历的支持,这些日历允许13个月,因此它取决于您所谈论的日历系统
例如,MONTH字段的实际最大值在某些年份 12 ,希伯来日历中其他年份的 13 系统。所以第13个月是有效的
答案 2 :(得分:1)
在没有给定parse
的情况下调用TemporalQuery
时,不会抛出异常,这有点奇怪。单个参数parse
方法的一些文档:
这解析生成临时对象的整个文本。使用parse(CharSequence,TemporalQuery)通常更有用。此方法的结果是TemporalAccessor已经解决,应用基本验证检查以帮助确保有效的日期时间。
请注意,它表示使用解析(CharSequence,TemporalQuery)"通常更有用。在您的示例中,parse
返回了一个java.time.format.Parsed
对象,除了创建不同的TemporalAccessor
之外,该对象并未真正用于其他任何内容。
请注意,如果您尝试从返回的值创建YearMonth
,则会引发异常:
YearMonth.from(DateTimeFormatter.ofPattern(format)
.withResolverStyle(ResolverStyle.STRICT).parse(value));
引发
Exception in thread "main" java.time.DateTimeException: Unable to obtain YearMonth from TemporalAccessor: {Year=2017, MonthOfYear=50},ISO of type java.time.format.Parsed
at java.time.YearMonth.from(YearMonth.java:263)
at anl.nfolds.Test.main(Test.java:21)
Caused by: java.time.DateTimeException: Invalid value for MonthOfYear (valid values 1 - 12): 50
at java.time.temporal.TemporalAccessor.get(TemporalAccessor.java:224)
at java.time.YearMonth.from(YearMonth.java:260)
... 1 more
Parsed
的文档:
解析数据的存储。
在解析过程中使用此类来收集数据。解析过程的一部分涉及处理可选块,并创建数据的多个副本以支持必要的回溯。
解析完成后,此类可用作生成的TemporalAccessor。在大多数情况下,只有在字段解析后才会显示它。
自:1.8
@implSpec这个类是一个可变的上下文,旨在从单个线程使用。在标准解析中,类的用法是线程安全的,因为为每个解析自动创建此类的新实例,并且解析是单线程的