2017-12-27 1477 views
4

我的要求是基于指定的一组有效格式来验证日期字符串格式是否正确。Java 8 DateTimeFormatterBuilder()。appendOptional不工作

有效格式:

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")) 
.appendOptional(DateTimeFormatter.ofPattern("MM/dd/yyyy")) 

,但仍然有同样的问题。

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

String dateString = "10/30/17"; 

代替

String dateString = "10/30/2017"; 

我有2个问题

  1. 这是怎么回事错在这里?为什么它不适用于“10/30/2017”?如何正确创建一个灵活的日期格式化程序(一个支持多种可选格式的格式化程序)?我知道使用[]在模式字符串本身中创建可选部分。我在找东西更类似于我想(避免[模式字符串中]和使用单独的可选子句对每个单独的格式字符串)

+0

这不是'appendOptional()'应该如何使用(请参阅下面的答案)。为了允许不同的日期格式,最简单的方法可能是创建一个格式化程序列表,但我不确定这是最好的方法。 –

回答

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 

==

这只是一个简单的解决方案,如果性能是您的顾虑,通过捕获解析异常的验证应该是las吨景区

  • 你可能首先执行日期字符串解析
  • 你还可以用含有简单的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; 
    }); 
    
+0

有道理,我下次在笔记本电脑上时会删除我的答案 - 但如果您提供了OP *应该使用的代码,答案会更好。 –

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 */