Java 8 DateTimeFormatterBuilder()。appendOptional not working

时间:2017-12-27 19:37:05

标签: java date datetime datetime-format java-date

我的要求是根据指定的一组有效格式验证日期字符串的格式是否正确。

有效格式:

MM/dd/yy
MM/dd/yyyy

我创建了一个简单的测试方法,它使用Java 8 DateTimeFormatterBuilder创建一个支持多种可选格式的灵活格式化程序。这是代码:

public static void test() {
    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy"))
            .appendOptional(DateTimeFormatter.ofPattern("MM/dd/yyyy"))
            .toFormatter();

    String dateString = "10/30/2017";

    try {
        LocalDate.parse(dateString, formatter);
        System.out.println(dateString + " has a valid date format");
    } catch (Exception e) {
        System.out.println(dateString + " has an invalid date format");
    }
}

当我运行它时,这是输出

10/30/2017 has an invalid date format

正如您在代码中看到的那样,有效日期格式为MM / dd / yy和MM / dd / yyyy。 我的期望是10/30/2017的日期应该有效,因为它与MM / dd / yyyy匹配。但是,据报道,10/30/2017无效。

出了什么问题?为什么这不起作用?

我也试过

.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy[yy]"))

取代

.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy"))
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yyyy"))

但仍有同样的问题。

如果我使用以下代码,则此代码按预期运行:

String dateString = "10/30/17";

取代

String dateString = "10/30/2017";

我有两个问题

  1. 这里出了什么问题?为什么它不适用于" 10/30/2017"

  2. 使用Java 8,如何正确创建灵活的Date格式化程序(支持多种可选格式的格式化程序)?我知道使用[]在模式字符串本身中创建可选节。我正在寻找更类似于我正在尝试的东西(避免在模式字符串中使用[]并为每个单独的格式字符串使用单独的可选子句)

2 个答案:

答案 0 :(得分:3)

格式化程序无法按预期工作,可选部分表示

  • 如果第一个模式没有附加任何额外的东西(例如,“MM / dd / yy”),那很好,
  • 如果有额外的东西,则需要匹配第二种模式(例如“MM / dd / yyyy”)

为了使它更清晰,请尝试运行下面的示例代码以更好地理解它:

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy"))
            .appendOptional(DateTimeFormatter.ofPattern("MM/dd/yyyy"))
            .toFormatter();

    String[] dateStrings = {
            "10/30/17",           // valid
            "10/30/2017",         // invalid
            "10/30/1710/30/2017", // valid
            "10/30/201710/30/17"  // invalid
    };

    for (String dateString : dateStrings) {
        try {
            LocalDate.parse(dateString, formatter);
            System.out.println(dateString + " has a valid date format");
        } catch (Exception e) {
            System.err.println(dateString + " has an invalid date format");
        }
    }

==

10/30/17 has a valid date format
10/30/1710/30/2017 has a valid date format
10/30/2017 has an invalid date format
10/30/201710/30/17 has an invalid date format

==

这只是一个简单的解决方案,如果您关心性能,通过捕获解析异常进行验证应该是最后的手段

  • 您可以在执行日期字符串解析之前先按长度或正则表达式检查字符串
  • 您也可以使用包含简单for循环等的方法替换流

    String[] patterns = { "MM/dd/yy", "MM/dd/yyyy" };
    Map<String, DateTimeFormatter> formatters = Stream.of(patterns).collect(Collectors.toMap(
            pattern -> pattern, 
            pattern -> new DateTimeFormatterBuilder().appendOptional(DateTimeFormatter.ofPattern(pattern)).toFormatter()
    ));
    
    String dateString = "10/30/17";
    boolean valid = formatters.entrySet().stream().anyMatch(entry -> {
        // relying on catching parsing exception will have serious expense on performance
        // a simple check will already improve a lot 
        if (dateString.length() == entry.getKey().length()) {
            try {
                LocalDate.parse(dateString, entry.getValue());
                return true;
            }
            catch (DateTimeParseException e) {
                // ignore or log it   
            }
        }
        return false;
    });
    

答案 1 :(得分:0)

构建器的appendValueReduced()方法旨在处理此案例。

解析字段的完整值时,格式化程序会将其视为绝对值。

解析字段的部分值时,格式化程序将相对于您指定的基础解释它。例如,如果您希望将两位数年份解释为1970年到2069年之间,则可以指定1970作为基数。这是一个例子:

    LocalDate century = LocalDate.ofEpochDay(0); /* Beginning Jan. 1, 1970 */
    DateTimeFormatter f = new DateTimeFormatterBuilder()
            .append(DateTimeFormatter.ofPattern("MM/dd/"))
            .appendValueReduced(ChronoField.YEAR, 2, 4, century)
            .toFormatter();
    System.out.println(LocalDate.parse("10/30/2017", f)); /* 2017-10-30 */
    System.out.println(LocalDate.parse("10/30/17", f));   /* 2017-10-30 */
    System.out.println(LocalDate.parse("12/28/1969", f)); /* 1969-12-28 */
    System.out.println(LocalDate.parse("12/28/69", f));   /* 2069-12-28 */