Java 8 LocalDateTime解析无效的日期时间。

34
我想在客户端验证日期时间,所以我写了以下代码。但是,与其得到一个异常,我得到了一个正确的日期时间对象,对于2月31日的日期字符串来说,这显然是一个无效的日期。
public class Test {

    public static void main(String[] args) {
        String dateFormat = "HH:mm:ss MM/dd/yyyy";
        String dateString = "11:30:59 02/31/2015";
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US);
        try {
            LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
            System.out.println(date);
        } catch (Exception e) {
            // Throw invalid date message
        }
    }
}

输出:2015-02-28T11:30:59

有人知道为什么LocalDateTime在解析这个日期时间时不会抛出异常吗?


4
@AndrewTobilko,大概是因为2月31日并不存在。 - aioobe
但是格式化程序不知道这一点。 - SatyaTNV
好奇一下,02/29/2015(同样不存在的日期)的表现是否符合预期? - Foon
4
@Satya:你为什么会这样想?显然有些东西知道那个日期并不存在,因为它把它转换成了28日... - Jon Skeet
1
@JonSkeet 对之前误导您表示歉意,问题并不是缺少毫秒。实际上是缺少了纪元。仍需要使用 DateTimeFormatterBuilder,但默认为纪元而非毫秒。如果您想编辑和恢复您的答案,请随意。 - RealSkeptic
显示剩余2条评论
5个回答

50

你只需要一个严格的ResolverStyle

文本字符串解析包含两个阶段。第一阶段是根据添加到构建器中的字段进行基本的文本解析。第二阶段将解析的字段-值对解析为日期和/或时间对象。此样式用于控制第二阶段——解析的发生方式。

示例代码 - 其中withResolverStyle(ResolverStyle.STRICT)是重要更改,以及使用uuuu而不是yyyy(其中uuuu是“年”而yyyy是“纪元年”,因此会出现歧义):

import java.time.*;
import java.time.format.*;
import java.util.*;

public class Test {

    public static void main(String[] args) {
        String dateFormat = "HH:mm:ss MM/dd/uuuu";
        String dateString = "11:30:59 02/31/2015";
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter
            .ofPattern(dateFormat, Locale.US)
            .withResolverStyle(ResolverStyle.STRICT);
        try {
            LocalDateTime date = LocalDateTime.parse(dateString, dateTimeFormatter);
            System.out.println(date);
        } catch (DateTimeParseException e) {
            // Throw invalid date message
            System.out.println("Exception was thrown");
        }
    }
}

6
将格式字符串中的"yyyy"替换为"uuuu",这样就不需要去处理纪元了。 - Palamino

11

Java 8的DateTimeFormatter使用yyyy表示纪元年份,而uuuu表示年份。您需要按以下方式修改模式字符串:

Java 8的DateTimeFormatter使用yyyy表示纪元年份,而uuuu表示年份。您需要按以下方式修改模式字符串:

String dateFormat = "HH:mm:ss MM/dd/uuuu";

DateTimeFormatter默认使用SMART解析器样式,但你想要它使用STRICT解析器样式。请按照以下方式修改你的dateTimeFormatter初始化代码:

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateFormat, Locale.US)
                                                       .withResolverStyle(ResolverStyle.STRICT);

3

它没有进行四舍五入。二月从未有31天,使用验证日期/时间对象表示不存在的日期是不可能的。

因此,它会接受无效的输入并给出最佳近似正确日期的结果(即那一年二月的最后一天)。

SimpleDateFormat继承自DateFormat,它具有setLenient(boolean value)方法。如果您在解析之前调用了setLenient(true),它可能会更加严格,如Java文档中所述


2
try {
    SimpleDateFormat df = new java.text.SimpleDateFormat("HH:mm:ss MM/dd/yyyy");
    df.setLenient(false);
    System.out.println(df.parse("11:30:59 02/29/2015"));
} catch (java.text.ParseException e) {
  System.out.println(e);
}

我找到了一个解决方案,可以使用DateFormat.setLenient(boolean)来将日期识别为有效日期。如果您尝试解析任何无效日期,它都会抛出解析异常。
编辑:在Java 8中,但是如果月份不在1到12之间或者天数大于32,则会引发异常。它并不能完全工作,但对于月份是有效的。
try {
TemporalAccessor ta = DateTimeFormatter.ofPattern("HH:mm:ss MM/dd/yyyy").parse("11:30:59 02/32/2015");
} catch (Exception e) {
System.out.println(e);
}

输出:

java.time.format.DateTimeParseException: Text '11:30:59 02/32/2015' could not be
 parsed: Invalid value for DayOfMonth (valid values 1 - 28/31): 32

2
SimpleDateFormat在Java 8 LocalDateTime中不起作用。我正在寻找使用Java8 LocalDateTime的解决方案。 - Zeeshan

0

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