我的程序正在将输入字符串解析为LocalDate
对象。大多数情况下,字符串看起来像30.03.2014
,但偶尔看起来像3/30/2014
。根据具体情况,我需要使用不同的模式来调用DateTimeFormatter.ofPattern(String pattern)
。基本上,在进行解析之前,我需要检查字符串是否与模式dd.MM.yyyy
或M/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
的文档,但我找不到任何内容。
答案 0 :(得分:12)
好的,我要继续发布并作为答案发布。一种方法是创建将保存模式的类。
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");
}
}
您可以通过直接从构造函数中传递的DateTimeFormatter
数组中创建String
数组来改进这一点。
另一种方法是使用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
答案 1 :(得分:1)
@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
- 行为一致。
不幸的是,替代且更直观的方法DateTimeFormater.parse()首先创建DateTimeParseException
,然后将错误索引存储在此异常中。所以我决定不使用这种方法来避免产生不必要的异常。对我来说,这个API细节是一个值得怀疑的设计决定。
答案 2 :(得分: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
从 modern date-time API 中了解有关 Trail: Date Time* 的更多信息。
* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 & 7. 如果您正在为 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaring 和 How to use ThreeTenABP in Android Project。