现有年份、可选月份和可选日期的 DateTimeFormatter 验证

问题描述 投票:0回答:1

我们正在尝试编写一个 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));
    }
}
java java-time datetimeformatter
1个回答
0
投票

您可以通过打印

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 来说,这部分是否可以解析到有效的月份并不重要。

© www.soinside.com 2019 - 2024. All rights reserved.