我正在尝试编写一个类,它能够将多格式和多语言环境字符串解析为DateTime
。
multi-format
表示日期可能是:dd/MM/yyyy
,MMM dd yyyy
,...(最多10种格式)
multi-locale
表示日期可能是:29 Dec 2015
,29 Dez 2015
,dice 29 2015
...(最多10个区域设置,例如en
,{{1 }},gr
,it
)
使用我写的答案Using Joda Date & Time API to parse multiple formats:
jp
但它不起作用:
val locales = List(
Locale.ENGLISH,
Locale.GERMAN,
...
)
val patterns = List(
"yyyy/MM/dd",
"yyyy-MM-dd",
"MMMM dd, yyyy",
"dd MMMM yyyy",
"dd MMM yyyy"
)
val parsers = patterns.flatMap(patt => locales.map(locale => DateTimeFormat.forPattern(patt).withLocale(locale).getParser)).toArray
val birthDateFormatter = new DateTimeFormatterBuilder().append(null, parsers).toFormatter
我发现所有birthDateFormatter.parseDateTime("29 Dec 2015") // ok
birthDateFormatter.parseDateTime("29 Dez 2015") // exception below
Invalid format: "29 Dez 2015" is malformed at "Dez 2015"
java.lang.IllegalArgumentException: Invalid format: "29 Dez 2015" is
malformed at "Dez 2015"
都失去了#34;附加到parsers: List[DateTimeParser]
后的语言环境。 birthDateFormatter: DateTimeFormatter
只有一个区域设置 - birthDateFormatter
。
我可以写:
en
并使用它:
val birthDateFormatter = locales.map(new DateTimeFormatterBuilder().append(null, parsers).toFormatter.withLocale(_))
但它会引发很多例外。太可怕了。
如何使用joda-time解析多格式和多语言环境字符串? 我怎么能以其他方式做到这一点?
答案 0 :(得分:3)
调查很有意思。这是一个帮助我的测试套件(在Java中,但我希望你能得到这个想法):
birthDateFormatter.parseDateTime("29 Dec 2015")
首先,你的第一个例子
DateTimeFormat.forPattern("dd MMM yyyy").withLocale(locale).getParser()
仅仅因为您的计算机的默认语言环境是英语而通过。如果不同,这种情况也会失败。这就是我在使用英语语言环境的机器上运行时使用法语和德语的原因。就我而言,两个断言都失败了。
事实证明,语言环境不存储在解析器中,而只存储在格式化程序中。所以当你这样做时
// DateTimeFormatter#withLocale:
public DateTimeFormatter withLocale(Locale locale) {
if (locale == getLocale() || (locale != null && locale.equals(getLocale()))) {
return this;
}
// Notice how locale does not affect the parser
return new DateTimeFormatter(iPrinter, iParser, locale,
iOffsetParsed, iChrono, iZone, iPivotYear, iDefaultYear);
}
语言环境在格式化程序上设置,但在创建解析器时会丢失:
new DateTimeFormatterBuilder().append(null, parsers).toFormatter()
接下来,当您创建新的格式化程序
时withLocale()
它是使用系统的默认语言环境创建的(除非用// DateTimeFormatter#parseDateTime
public DateTime parseDateTime(String text) {
InternalParser parser = requireParser();
Chronology chrono = selectChronology(null);
// Notice how the formatter's locale is used
DateTimeParserBucket bucket = new DateTimeParserBucket(0, chrono, iLocale, iPivotYear, iDefaultYear);
int newPos = parser.parseInto(bucket, text, 0);
// ... snipped
}
覆盖它)。在解析过程中使用该语言环境:
{{1}}
事实证明,尽管您可以使用多个解析器来支持多种格式,但每个格式化程序实例仍然只能使用一个语言环境。
答案 1 :(得分:1)
回答问题1(如何使用joda-time解析多格式和多语言环境的字符串?):
不,这不可能以你想要的方式,请参阅@Adam Michalik的好答案。因此,唯一的方法就是编写一个包含多个Joda格式化程序的列表,并针对给定的输入尝试每个格式化程序 - 可能会捕获异常。您已找到正确的解决方法,因此我不会在此处描述详细信息。
回答问题2(我怎么能以其他方式做到?):
我的图书馆Time4J从v4.11开始有一个新的MultiFormatParser
- 类。但是,我发现它的格式引擎存在一些性能问题(主要是由于Java的自动装箱功能)所以我决定等到这个答案,直到发布v4.12,我已经改进了性能。 根据我的第一个基准测试,Time4J-4.12似乎比Joda-Time(v2.9.1)更快,因为内部异常大大减少了。因此,我认为您可以尝试使用最新版本的Time4J,然后报告一些反馈,如果它适合您。
private static final MultiFormatParser<PlainDate> TIME4J;
static {
ChronoFormatter<PlainDate> f1 =
ChronoFormatter.ofDatePattern("dd.MM.uuuu", PatternType.CLDR, Locale.ROOT);
ChronoFormatter<PlainDate> f2 =
ChronoFormatter.ofDatePattern("MM/dd/uuuu", PatternType.CLDR, Locale.ROOT);
ChronoFormatter<PlainDate> f3 =
ChronoFormatter.ofDatePattern("uuuu-MM-dd", PatternType.CLDR, Locale.ROOT);
ChronoFormatter<PlainDate> f4 =
ChronoFormatter.ofDatePattern("uuuuMMdd", PatternType.CLDR, Locale.ROOT);
ChronoFormatter<PlainDate> f5 =
ChronoFormatter.ofDatePattern("d. MMMM uuuu", PatternType.CLDR, Locale.GERMAN);
ChronoFormatter<PlainDate> f6 =
ChronoFormatter.ofDatePattern("d. MMMM uuuu", PatternType.CLDR, Locale.FRENCH);
ChronoFormatter<PlainDate> f7 =
ChronoFormatter.ofDatePattern("MMMM d, uuuu", PatternType.CLDR, Locale.US);
TIME4J = MultiFormatParser.of(f1, f2, f3, f4, f5, f6, f7);
}
...
static List<PlainDate> parse(List<String> input) {
ParseLog plog = new ParseLog();
int n = input.size();
List<PlainDate> result = new ArrayList<>(n);
for (int i = 0; i < n; i++){
String s = input.get(i);
plog.reset();
PlainDate date = TIME4J.parse(s, plog);
if (!plog.isError()) {
result.add(date);
} else {
// log or report error
}
}
return result;
}
MultiFormatParser
中的每个解析器都保留自己的区域设置。MultiFormatParser
使用静态常量,因为a)它是不可变的,b)构建格式化程序在每个库中都很昂贵(而且Time4J对此细节也不例外) )。LocalDate joda = new LocalDate(plainDate.getYear(), plainDate.getMonth(), plainDate.getDayOfMonth());
但请注意,每次转化都会产生一些额外费用。另一方面,Joda-Time提供的功能少于Time4J,因此后者可以完成所有日期 - 时区相关任务的全部工作。val parser = MultiFormatParser.of(patterns.flatMap(patt => locales.map(locale => ChronoFormatter.ofDatePattern(patt, PatternType.CLDR, locale))).toArray)
java.time
)构建的新时间库在性能方面是最差的。