更漂亮的Multiple DateTimeFormatter



我得到了多个字符串日期来转换为OffsetDateTime,我用了多次try-and-catch,我想我不会有其他DateTimeFormatter要写。那么,如何让它变得更加美丽呢?

代码:

public static OffsetDateTime convertStringDateToOffsetDate(String dateStr){
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withLocale( Locale.US );
DateTimeFormatter f2 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss").withZone(ZoneId.of("Europe/Paris"));
DateTimeFormatter f3 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX").withZone(ZoneId.of("Europe/Paris"));
DateTimeFormatter f4 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSS").withZone(ZoneId.of("Europe/Paris"));
DateTimeFormatter f5 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS").withZone(ZoneId.of("Europe/Paris"));
DateTimeFormatter f6 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSXXX").withZone(ZoneId.of("Europe/Paris"));
OffsetDateTime myDate = null;
try{
myDate = ZonedDateTime.parse(dateStr, f).toOffsetDateTime();
} catch(DateTimeParseException e){
try{
myDate = ZonedDateTime.parse(dateStr, f2).toOffsetDateTime();
} catch (DateTimeParseException ex) {
try{
myDate = ZonedDateTime.parse(dateStr, f3).toOffsetDateTime();
} catch (DateTimeParseException exc) {
try{
myDate = ZonedDateTime.parse(dateStr, f4).toOffsetDateTime();
}  catch (DateTimeParseException exce) {
try{
myDate = ZonedDateTime.parse(dateStr, f5).toOffsetDateTime();
} catch(DateTimeParseException excep){
myDate = ZonedDateTime.parse(dateStr, f6).toOffsetDateTime();
}
}
}
}
}
return myDate;
}
public static OffsetDateTime convertStringDateToOffsetDate(String dateStr){
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd['T'][ ][HH:mm:ss][.][SSSSSS][SSSSS][SSSS][SSS][XXX][XX][X]").withZone(ZoneId.of("Europe/Paris"));
return ZonedDateTime.parse(dateStr, f).toOffsetDateTime();
}

这应该可以处理您的所有模式。不需要多个格式化程序或正则表达式。

您可以使用[]语法将格式字符串的部分声明为可选。这可能只是让你找到一个单一的模式来处理这一切。然而,在这种设置中,一个模式有US语言环境,而其他模式没有,这部分将不适合单个格式的字符串。因此,您可以减少格式字符串的数量,但可能不会减少到一个。

然后,使用列表和助手方法来实现干净的代码:

private static final List<DateTimeFormatter> FORMATS = List.of(
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX").withLocale( Locale.US ),
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.][SSSSSS][XXX]").withZone(ZoneId.of("Europe/Paris")));
public static OffsetDateTime parse(String dateStr) throws DateTimeParseException {
DateTimeParseException ex = null;
for (var format : FORMATS) try {
return ZonedDateTime.parse(dateStr, format).toOffsetDateTime();
} catch (DateTimeParseException e) {
ex = e;
}
throw ex;
}

这是我的尝试。

private static final DateTimeFormatter PARSER = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
.optionalStart()
.appendOffsetId()
.optionalEnd()
.toFormatter(Locale.ROOT);
private static final ZoneId DEFAULT_ZONE = ZoneId.of("Europe/Paris");
public static OffsetDateTime convertStringDateToOffsetDate(String dateStr) {
TemporalAccessor parsed
= PARSER.parseBest(dateStr, OffsetDateTime::from, LocalDateTime::from);
if (parsed instanceof OffsetDateTime) {
return (OffsetDateTime) parsed;
} else {
return ((LocalDateTime) parsed).atZone(DEFAULT_ZONE).toOffsetDateTime();
}
}

尝试一下:

String[] testStrings = {
"2021-01-01T12:34:56.789-07:00",
"2021-02-01T12:34:56",
"2021-03-01T12:34:56-06:00",
"2021-04-01T12:34:56.987654",
"2021-05-01T12:34:56.789",
"2021-06-01T12:34:56.987654-05:00"
};

for (String testString : testStrings) {
System.out.format("%-32s -> %s%n", testString, convertStringDateToOffsetDate(testString));
}

输出:

2021-01-01T12:34:56.789-07:00    -> 2021-01-01T12:34:56.789-07:00
2021-02-01T12:34:56              -> 2021-02-01T12:34:56+01:00
2021-03-01T12:34:56-06:00        -> 2021-03-01T12:34:56-06:00
2021-04-01T12:34:56.987654       -> 2021-04-01T12:34:56.987654+02:00
2021-05-01T12:34:56.789          -> 2021-05-01T12:34:56.789+02:00
2021-06-01T12:34:56.987654-05:00 -> 2021-06-01T12:34:56.987654-05:00

您注意到:

  • 它处理您问题中的所有6种格式
  • 对于具有UTC偏移量的字符串,将保留该偏移量。对于没有的字符串,假设Paris的正确偏移量(二月+01:00,四月和五月+02:00(

我相信它有以下优点:

  • 我只需要一个格式化程序
  • 我根本没有写任何格式模式字符串,只是用内置部件组装了我的格式化程序

我用来解析的DateTimeFormatter.parseBest方法将首先尝试创建OffsetDateTime,如果不成功,它将求助于创建并返回LocalDateTime。在后一种情况下,我需要转换它。我的解决方案的缺点是,我需要通过TemporalAccessor,这是一个我认为是低级的接口,我们通常不应该在应用程序代码中使用。

内置的DateTimeFOrmatter.ISO_LOCAL_DATE_TIME已经处理了秒上最多9位小数的存在和不存在。因此,通过在我的格式化程序中重用它,我已经处理了无小数以及3和6小数的情况。

对于没有偏移量的字符串,要求使用欧洲/巴黎时区的一个挑战是,虽然DateTimeFormatter可以有许多默认值,但它不能有默认时区。withZone方法为我们提供了一个带有覆盖区域的格式化程序,但这是另一回事。该格式化程序将在格式化或解析的结果上强制执行覆盖区域。虽然你的问题不清楚,但我认为你不想要这个。

编辑:格式化程序是否需要区域设置我使用.toFormatter(Locale.ROOT)从构建器构建格式化程序。从技术上讲,在这种情况下,区域设置是不必要的,因为我的格式化程序不包括任何依赖于区域设置的部分,并且在这个答案的第一个版本中,我忽略了区域设置(而是调用无参数toFormatter方法(。然而,我倾向于同意Arvind Kumar Avinash的评论:

只是小挑剔:请始终使用带日期时间的Locale正在解析/格式化类型…,因为它是Locale敏感类型。它可能与此中处理的日期时间字符串无关解决方案,但我们应该像遵守规则一样坚持下去。

这可能只是我的傲慢,并认为读者能够确定格式化程序中没有区域设置敏感部分。提供一个区域设置是更好的习惯(否则,至少要记住为什么没有区域设置(。

您可以在循环中使用一个try-catch,在循环中会忽略异常。

List<DateTimeFormatter> list = Arrays.toList<>(f, f1, f2, f3, f4, f5, f6);
for(DateTimeFormatter formatter : list)
{
try
{
myDate = ZonedDateTime.parse(dateStr, formatter).toOffsetDateTime();
break;
}
catch(Exception e)
{
}
}

但请记住,异常的性能很差(将stacktrace写入变量需要时间(,所以M.Dudek关于使用regex的注释可能是更好的答案。

相关内容

  • 没有找到相关文章

最新更新