我们正在尝试编写一个 DateTimeFormatter 来帮助我们验证 ISO 8601,该标准允许我们的最终用户仅输入年、年和月,或年、月和日。我们还想验证输入的日期是否确实存在。
在下面的代码中有两个日期和日期验证表现得很时髦的示例。第一个是仅针对一年和可选月份的验证测试。这不能正确验证月份“00”。
第二个示例显示测试(不是)因错误的可选月份值而失败。
任何友好的指导将不胜感激。
import org.junit.jupiter.api.Test;
import java.time.LocalDate;
import java.time.Year;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.time.format.ResolverStyle;
import java.time.temporal.TemporalQuery;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class DateTimeFormatterForStackOverflowTest {
@Test
public void test_year_or_year_and_month_not_valid() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("uuuu[-MM]")
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT);
this.expectException("1984-0", formatter, YearMonth::from, Year::from);
// Doesn't throw exception.
this.expectException("1984-00", formatter, YearMonth::from, Year::from);
this.expectException("1984-13", formatter, YearMonth::from, Year::from);
this.expectException("1984-99", formatter, YearMonth::from, Year::from);
}
@Test
public void test_year_or_year_month_or_year_month_day_not_valid() {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("[uuuu-MM-dd][uuuu-MM][uuuu]")
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT);
this.expectException("1984-0", formatter, LocalDate::from, YearMonth::from, Year::from);
// Doesn't throw exception.
this.expectException("1984-00", formatter, LocalDate::from, YearMonth::from, Year::from);
// Doesn't throw exception.
this.expectException("1984-13", formatter, LocalDate::from, YearMonth::from, Year::from);
// Doesn't throw exception.
this.expectException("1984-99", formatter, LocalDate::from, YearMonth::from, Year::from);
this.expectException("1984-00-01", formatter, LocalDate::from, YearMonth::from, Year::from);
this.expectException("1984-13-01", formatter, LocalDate::from, YearMonth::from, Year::from);
this.expectException("1984-01-0", formatter, LocalDate::from, YearMonth::from, Year::from);
this.expectException("1984-01-00", formatter, LocalDate::from, YearMonth::from, Year::from);
this.expectException("1984-01-32", formatter, LocalDate::from, YearMonth::from, Year::from);
this.expectException("1984-12-00", formatter, LocalDate::from, YearMonth::from, Year::from);
this.expectException("1984-12-32", formatter, LocalDate::from, YearMonth::from, Year::from);
}
private void expectException(String value, DateTimeFormatter formatter, TemporalQuery<?>... queries) {
assertThrows(DateTimeParseException.class,
() -> formatter.parseBest(value, queries));
}
}
您可以通过打印
parseBest
结果的类型来了解发生了什么:
assertThrows(DateTimeParseException.class,
() -> System.out.println(
formatter.parseBest(value, queries).getClass()));
结果是:
java.time.Year
正如您所料,“1984-00”无法解析为 YearMonth。但是您将
YearMonth::from, Year::from
作为 TemporalQuerys 传递,因此在 parseBest 发现无法使用 YearMonth.from 解析该字符串后,它再次尝试使用 Year.from,结果成功。
为什么会成功?因为 Year 只需要解析年份部分(DateTimeFormatter 中的
uuuu
)。从未尝试解析月份,因此 00
被忽略。对于 Year 来说,这部分是否可以解析到有效的月份并不重要。