解析 java.time 尝试多种模式
Parse java.time trying multiple patterns
我们有一个库,用户可以在其中传递多种格式的日期。它们遵循 ISO,但有时会缩写。
所以我们得到诸如“19-3-12”和“2019-03-12T13:12:45.1234”之类的东西,其中小数秒的长度可以是 1 - 7 位。这是非常多的组合。
DateTimeFormatter.parseBest 不起作用,因为它不接受 "yy-m-d" 作为本地日期。 不会工作,因为它假设我们知道模式 - 我们不知道。
并且告诉人们获取他们的字符串格式 "correct" 是行不通的,因为存在大量现有数据(这些数据主要在 XML 和 JSON 文件中)。
我的问题是,如何在不必尝试 15 种不同的显式模式的情况下解析以这些不同模式出现的字符串?
或者更好的是,是否有某种方法可以解析字符串,它会尝试所有可能的方法,如果字符串对任何日期[时间]都有意义,return 一个 Temporal 对象?
尝试所有可能的格式比只尝试 15 种格式效果更差。
您可以尝试 "normalize" 到单一格式,但那样您将完成这 15 种格式应该完成的工作。
我认为最好的方法是@JB Nizet 描述的方法,即只尝试匹配字符串长度的模式。
public Date parse(String openFormat) {
String[] formats = {"YYY-MM-DD"};
switch(openFormat.length()) {
case 24: // 2019-03-12T13:12:45.1234
formats = new String[]{"YYY-MM-DDThh:mm:ssetcetc", }; // all the formats for length 24
break;
...
case 6: //YYY-MM-DD, DD-MM-YYYY
formats = new String[]{YYY-MM-DD", "DD-MM-YYYY", }; // all the formats for length 6
break;
}
Date myDate
// now try the reduced number of formats, possibly only 1 or 2
for( String format : formats) try {
myDate = date parse ( format ) etcetc
} catch (DateFormatException d) {
continue;
}
if (myDate == null){
throw InvalidDate
} else {
return myDate
}
}
没有完整的规格,很难给出准确的建议。通常用于可变格式的技术包括:
- 依次尝试多种已知格式。
- 格式模式中的可选部分。
DateTimeFormatterBuilder.parseDefaulting()
对于解析后的字符串中可能不存在的部分。
- 如您所知,
parseBest
。
我假设 y-M-d 始终按此顺序出现(例如,永远不会是 M-d-y 或 d-M-y)。 19-3-12
与 ISO 8601 冲突,因为该标准要求(至少)4 位数字年份和 2 位数字月份。 2 位数年份的挑战是猜测世纪:这是 1919 年还是 2019 年,或者可能是 2119 年?
好消息:秒的存在与否以及小数位数的不同数量都是内置的,不会造成任何问题。
根据您告诉我们的内容,在我看来,以下是一个不错的选择。
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("[uuuu][uu]-M-d")
.optionalStart()
.appendLiteral('T')
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.optionalEnd()
.toFormatter();
TemporalAccessor dt = formatter.parseBest("19-3-12", LocalDateTime::from, LocalDate::from);
System.out.println(dt.getClass());
System.out.println(dt);
输出:
class java.time.LocalDate
2019-03-12
我认为它应该适用于您描述的各种格式。让我们试试你的另一个例子:
dt = formatter.parseBest( "2019-03-12T13:12:45.1234", LocalDateTime::from, LocalDate::from);
System.out.println(dt.getClass());
System.out.println(dt);
class java.time.LocalDateTime
2019-03-12T13:12:45.123400
要控制 2 位数年份的解释,您可以使用 DateTimeFormatterBuilder.appendValueReduced()
的重载变体之一。我建议您考虑在其之上进行范围检查。
我们有一个库,用户可以在其中传递多种格式的日期。它们遵循 ISO,但有时会缩写。
所以我们得到诸如“19-3-12”和“2019-03-12T13:12:45.1234”之类的东西,其中小数秒的长度可以是 1 - 7 位。这是非常多的组合。
DateTimeFormatter.parseBest 不起作用,因为它不接受 "yy-m-d" 作为本地日期。
并且告诉人们获取他们的字符串格式 "correct" 是行不通的,因为存在大量现有数据(这些数据主要在 XML 和 JSON 文件中)。
我的问题是,如何在不必尝试 15 种不同的显式模式的情况下解析以这些不同模式出现的字符串?
或者更好的是,是否有某种方法可以解析字符串,它会尝试所有可能的方法,如果字符串对任何日期[时间]都有意义,return 一个 Temporal 对象?
尝试所有可能的格式比只尝试 15 种格式效果更差。
您可以尝试 "normalize" 到单一格式,但那样您将完成这 15 种格式应该完成的工作。
我认为最好的方法是@JB Nizet 描述的方法,即只尝试匹配字符串长度的模式。
public Date parse(String openFormat) {
String[] formats = {"YYY-MM-DD"};
switch(openFormat.length()) {
case 24: // 2019-03-12T13:12:45.1234
formats = new String[]{"YYY-MM-DDThh:mm:ssetcetc", }; // all the formats for length 24
break;
...
case 6: //YYY-MM-DD, DD-MM-YYYY
formats = new String[]{YYY-MM-DD", "DD-MM-YYYY", }; // all the formats for length 6
break;
}
Date myDate
// now try the reduced number of formats, possibly only 1 or 2
for( String format : formats) try {
myDate = date parse ( format ) etcetc
} catch (DateFormatException d) {
continue;
}
if (myDate == null){
throw InvalidDate
} else {
return myDate
}
}
没有完整的规格,很难给出准确的建议。通常用于可变格式的技术包括:
- 依次尝试多种已知格式。
- 格式模式中的可选部分。
DateTimeFormatterBuilder.parseDefaulting()
对于解析后的字符串中可能不存在的部分。- 如您所知,
parseBest
。
我假设 y-M-d 始终按此顺序出现(例如,永远不会是 M-d-y 或 d-M-y)。 19-3-12
与 ISO 8601 冲突,因为该标准要求(至少)4 位数字年份和 2 位数字月份。 2 位数年份的挑战是猜测世纪:这是 1919 年还是 2019 年,或者可能是 2119 年?
好消息:秒的存在与否以及小数位数的不同数量都是内置的,不会造成任何问题。
根据您告诉我们的内容,在我看来,以下是一个不错的选择。
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("[uuuu][uu]-M-d")
.optionalStart()
.appendLiteral('T')
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.optionalEnd()
.toFormatter();
TemporalAccessor dt = formatter.parseBest("19-3-12", LocalDateTime::from, LocalDate::from);
System.out.println(dt.getClass());
System.out.println(dt);
输出:
class java.time.LocalDate 2019-03-12
我认为它应该适用于您描述的各种格式。让我们试试你的另一个例子:
dt = formatter.parseBest( "2019-03-12T13:12:45.1234", LocalDateTime::from, LocalDate::from);
System.out.println(dt.getClass());
System.out.println(dt);
class java.time.LocalDateTime 2019-03-12T13:12:45.123400
要控制 2 位数年份的解释,您可以使用 DateTimeFormatterBuilder.appendValueReduced()
的重载变体之一。我建议您考虑在其之上进行范围检查。