2017-12-27 1332 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 */