宽松的Java 8日期解析

7

我想使用 LocalDateTime 解析 '2015-10-01'。我需要做的是:

LocalDate localDate = LocalDate.parse('2015-10-01');
LocalDateTime localDateTime = localDateTime.of(localDate, LocalTime.MIN);

但我想要一次性解析它,例如:

// throws DateTimeParseException
LocalDateTime date = LocalDateTime.parse('2015-10-01', DateTimeFormatter.ISO_LOCAL_DATE);

同样,一个字符串的微小差异也会抛出异常。
// throws DateTimeParseException
LocalDate localDate = LocalDate.parse("2015-9-5", DateTimeFormatter.ISO_LOCAL_DATE);

我能否使用Java 8日期API宽松地解析日期字符串?

4个回答

9

如果你想将日期字符串(如"2015-10-01""2015-9-5")解析为LocalDateTime对象,可以使用DateTimeFormatterBuilder创建自己的DateTimeFormatter

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                                        .appendPattern("yyyy")
                                        .appendLiteral('-')
                                        .appendValue(MONTH_OF_YEAR)
                                        .appendLiteral('-')
                                        .appendValue(DAY_OF_MONTH)
                                        .parseDefaulting(HOUR_OF_DAY, HOUR_OF_DAY.range().getMinimum())
                                        .parseDefaulting(MINUTE_OF_HOUR, MINUTE_OF_HOUR.range().getMinimum())
                                        .parseDefaulting(SECOND_OF_MINUTE, SECOND_OF_MINUTE.range().getMinimum())
                                        .parseDefaulting(NANO_OF_SECOND, NANO_OF_SECOND.range().getMinimum())
                                        .toFormatter();
System.out.println(LocalDateTime.parse("2015-9-5", formatter));
System.out.println(LocalDateTime.parse("2015-10-01", formatter));

每个字段的变长是由调用appendValue(field)处理的。引用 Javadoc 的话:

对于这样一个可变宽度值的解析器,通常会贪婪地进行处理,需要一位数字,但接受尽可能多的数字。

这意味着它能够解析以 1 或 2 个数字格式化的月份和日期。

为了构造一个 LocalDateTime,我们还需要向这个构建器提供一个 LocalTime。这是通过使用每个 LocalTime 字段的 parseDefaulting(field, value) 来完成的。这个方法接收一个字段和一个默认值,如果字符串中没有该字段,则选择该字段的默认值。因为在我们的情况下,时间信息不会在字符串中出现,因此将选择默认值,即该字段有效值范围的最小值(它是通过调用 getMinimum 来获取该字段的 ValueRange;也许我们在这里也可以硬编码为 0)。


如果要解析的字符串可能包含时间信息,我们可以使用 DateTimeFormatter 的可选节,如下所示:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                                        .appendPattern("yyyy")
                                        .appendLiteral('-')
                                        .appendValue(MONTH_OF_YEAR)
                                        .appendLiteral('-')
                                        .appendValue(DAY_OF_MONTH)
                                        .appendPattern("[ HH:mm]") // optional sections are surrounded by []
                                        .parseDefaulting(HOUR_OF_DAY, HOUR_OF_DAY.range().getMinimum())
                                        .parseDefaulting(MINUTE_OF_HOUR, MINUTE_OF_HOUR.range().getMinimum())
                                        .parseDefaulting(SECOND_OF_MINUTE, SECOND_OF_MINUTE.range().getMinimum())
                                        .parseDefaulting(NANO_OF_SECOND, NANO_OF_SECOND.range().getMinimum())
                                        .toFormatter();
System.out.println(LocalDateTime.parse("2015-9-5", formatter));
System.out.println(LocalDateTime.parse("2015-10-01", formatter));
System.out.println(LocalDateTime.parse("2015-1-1 10:10", formatter));

3
您的特定用例的简单解决方案是定义自己的格式。在这个示例中,即使我在我的模式中指定了一个月份的单一M和一个日期的单一d,它仍然将两个示例解析为相同的内容。
@Test
public void parsing_singleDigitDates_andDoubleDigitDates_areEqual()
{
    DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-M-d");

    LocalDate dt1 = LocalDate.parse("2015-09-05", fmt);
    LocalDate dt2 = LocalDate.parse("2015-9-5", fmt);

    Assert.assertEquals(dt1, dt2);
}

看到为了解决同样的问题,DateTimeFormatterBuilder所需编写的代码过于繁琐,我决定撰写这篇答案。


1
你可以尝试。
    private static final DateTimeFormatter formatter =
                DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss").withResolverStyle(ResolverStyle.LENIENT);

谢谢您的回答,但不是我想要的。为此,我必须以正确的格式提供文本。那种方法是有所不同的。 - Sanghyun Lee

0

解析日期和时间

要从字符串创建LocalDateTime对象,可以使用静态LocalDateTime.parse()方法。它接受一个字符串和一个DateTimeFormatter作为参数。 DateTimeFormatter用于指定日期/时间模式。

String str = "1986-04-08 12:00";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.parse(str, formatter);

格式化日期和时间

要从LocalDateTime对象创建格式化字符串,可以使用format()方法。

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.of(1986, Month.APRIL, 8, 12, 30);
String formattedDateTime = dateTime.format(formatter); // "1986-04-08 12:30"

请注意,DateTimeFormatter 中预定义了一些常用的日期/时间格式常量。例如:使用 DateTimeFormatter.ISO_DATE_TIME 格式化上面的 LocalDateTime 实例将会得到字符串 "1986-04-08T12:30:00"。
所有与日期/时间相关的对象(例如 LocalDate 或 ZonedDateTime)都可以使用 parse() 和 format() 方法。

1
很抱歉,请仔细阅读问题。我不是在询问用法。 - Sanghyun Lee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接