【问题标题】:How to check if string matches date pattern using time API?如何使用时间 API 检查字符串是否与日期模式匹配?
【发布时间】:2014-06-22 17:08:05
【问题描述】:

我的程序正在将输入字符串解析为LocalDate 对象。大多数情况下,字符串看起来像30.03.2014,但偶尔看起来像3/30/2014。根据具体情况,我需要使用不同的模式来调用DateTimeFormatter.ofPattern(String pattern)。基本上,在进行解析之前,我需要检查字符串是否与模式dd.MM.yyyyM/dd/yyyy 匹配。

正则表达式方法类似于:

LocalDate date;
if (dateString.matches("^\\d?\\d/\\d{2}/\\d{4}$")) {
  date = LocalDate.parse(dateString, DateTimeFormatter.ofPattern("M/dd/yyyy"));  
} else {
  date = LocalDate.parse(dateString, DateTimeFormatter.ofPattern("dd.MM.yyyy"));  
}

这可行,但在匹配字符串时也可以使用日期模式字符串。

是否有任何标准方法可以使用新的 Java 8 时间 API 来执行此操作,而无需使用正则表达式匹配?我查看了 DateTimeFormatter 的文档,但找不到任何东西。

【问题讨论】:

  • 你为什么不把replace("//", ".")串起来?
  • 我认为没有特定的方法可以检查日期是否遵循给定的模式。我会使用 SimpleDateFormat.parse 方法并检查 ParseException。如果您确定日期是正确的并且它是两种模式之一,您可以检查是否有“。”在字符串中。
  • @RobertNiestroj 因为月份和日期在两种模式中的位置不同。
  • @HermanTorjussen 我不会将此作为答案发布,因为我不知道您为此创建课程是否可行。是吗?像这样:tryjava8.com/app/snippets/5368a343e4b0753f2af0f806
  • @Max SimpleDateFormat 类不能直接解析为LocalDate,只能解析为java.util.Date,这是完全不同的。

标签: java java-8 datetime-format datetime-parsing java-time


【解决方案1】:

使用DateTimeFormatter,可以使用方括号指定可选模式。

演示:

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("[d.M.u][M/d/u][u-M-d]", Locale.ENGLISH);
        Stream.of(
                    "3/30/2014",
                    "30.03.2014",
                    "2014-05-14",
                    "14.05.2014"
        ).forEach(s -> System.out.println(LocalDate.parse(s, dtf)));
    }
}

输出:

2014-03-30
2014-03-30
2014-05-14
2014-05-14

通过 Trail: Date Time 了解有关 modern date-time API* 的更多信息。


* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7 . 如果您正在为一个 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project

【讨论】:

    【解决方案2】:

    好的,我将继续发布它作为答案。一种方法是创建将保存模式的类。

    public class Test {
        public static void main(String[] args){
            MyFormatter format = new MyFormatter("dd.MM.yyyy", "M/dd/yyyy");
            LocalDate  date = format.parse("3/30/2014"); //2014-03-30
            LocalDate  date2 = format.parse("30.03.2014"); //2014-03-30
        }
    }
    
    class MyFormatter {
        private final String[] patterns;
    
        public MyFormatter(String... patterns){
            this.patterns = patterns;
        }
    
        public LocalDate parse(String text){
            for(int i = 0; i < patterns.length; i++){
                try{
                    return LocalDate.parse(text, DateTimeFormatter.ofPattern(patterns[i]));
                }catch(DateTimeParseException excep){}
            }
            throw new IllegalArgumentException("Not able to parse the date for all patterns given");
        }
    }
    

    您可以像 @MenoHochschild 所做的那样改进这一点,方法是直接从您传入构造函数的 String 数组中创建一个 DateTimeFormatter 数组。


    另一种方法是使用DateTimeFormatterBuilder,附加您想要的格式。可能还有其他一些方法可以做到这一点,我没有深入阅读文档:-)

    DateTimeFormatter dfs = new DateTimeFormatterBuilder()
                               .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd"))                                                                 
                               .appendOptional(DateTimeFormatter.ofPattern("dd.MM.yyyy"))                                                                                     
                               .toFormatter();
    LocalDate d = LocalDate.parse("2014-05-14", dfs); //2014-05-14
    LocalDate d2 = LocalDate.parse("14.05.2014", dfs); //2014-05-14
    

    【讨论】:

    • 您的第二个选项效果很好,无需处理异常等。
    【解决方案3】:

    @ZouZou 的做法是一种可能的解决方案。

    为了尽可能避免在程序逻辑中使用异常(在性能方面也不太好),可以考虑遵循alternative

    static final String[] PATTERNS = {"dd.MM.yyyy", "M/dd/yyyy"};
    static final DateTimeFormatter[] FORMATTERS = new DateTimeFormatter[PATTERNS.length];
    
    static {
      for (int i = 0; i < PATTERNS.length; i++) {
        FORMATTERS[i] = DateTimeFormatter.ofPattern(PATTERNS[i]);
      }
    }
    
    public static LocalDate parse(String input) {
      ParsePosition pos = new ParsePosition();
      for (int i = 0; i < patterns.length; i++) {
        try {
          TemporalAccessor tacc = FORMATTERS[i].parseUnresolved(input, pos);
          if (pos.getErrorIndex < 0) {
            return LocalDate.from(tacc); // possibly throwing DateTimeException => validation failure
          }
        } catch (DateTimeException ex) { // catches also possible DateTimeParseException
          // go to next pattern
        }
        pos.setIndex(0);
        pos.setErrorIndex(-1);
      }
      throw new IllegalArgumentException("Input does not match any pattern: " + input);
    }
    

    更多解释parseUnresolved()方法:

    此方法只进行第一阶段的解析,因此没有包含初步验证或已解析字段组合工作的第二阶段。但是,LocalDate.from() 确实验证了每个输入,所以我认为这仍然足够。而且好处是parseUnresolved()使用了ParsePosition的错误索引。这与传统的java.text.Format-behaviour 一致。

    不幸的是,另一种更直观的方法DateTimeFormater.parse()首先创建了一个DateTimeParseException,然后将错误索引存储在这个异常中。所以我决定不使用这种方法以避免产生不必要的异常。对我来说,这个 API 细节是一个值得商榷的设计决定。

    【讨论】:

      猜你喜欢
      • 2010-12-19
      • 2016-10-03
      • 2012-09-17
      • 2018-04-03
      • 1970-01-01
      • 1970-01-01
      • 2010-12-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多