17

我的程序正在将输入字符串解析为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但找不到任何东西。

4

3 回答 3

17

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

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 那样通过直接DateTimeFormatterString您传入构造函数的数组中创建一个数组来改进这一点。


另一种方法是使用 a 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
于 2014-05-06T13:44:10.577 回答
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了解有关现代日期时间 API *的更多信息。


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

于 2021-05-08T12:05:11.763 回答
0

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

为了尽可能避免对程序逻辑使用异常(在性能方面也不是很好),可以考虑以下替代方案:

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

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

于 2014-05-06T12:19:40.193 回答