有没有办法确定DateTimeFormatter是仅仅是日期还是仅在构造之后的时间?

时间:2016-01-14 15:25:53

标签: java date java-8 java-time

使用Java 8的新日期时间库,将字符串解析为日期的方法是使用DateTimeFormatterLocalDateLocalTimeLocalDateTime都有一个静态解析方法,它接受String和格式化程序。一个潜在的问题是,如果你的DateTimeFormat不包含时间部分(或者对于DateTime,一个日期部分),即使你的模式匹配,你最终也会得到一个解析错误。

实施例

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYY-MM-DD");
LocalDateTime dt = LocalDateTime.parse("2016-01-11", formatter);

这会抛出DateTimeParseException(如所讨论的here)并带有消息

java.time.format.DateTimeParseException:
Text '2016-01-11' could not be parsed:
Unable to obtain LocalDateTime from TemporalAccessor

这是一个有点无用的错误消息,因为文本可以解析,因为它匹配模式。相反,错误与它无法创建LocalDateTime的时间部分这一事实有关。将代码重构为以下代码:

LocalDateTime ldt = LocalDate.parse("2016-01-11", formatter).atStartOfDay());

我的问题是,你说你有一个像这样的通用方法

 public static LocalDateTime getDate(String s, DateTimeFormatter format) {
  ...
  }

有没有办法确定需要调用哪个静态解析方法,通过应用一些默认逻辑将字符串强制转换为LocalDateTime?例如如果日期仅使用午夜,如果时间仅使用今天等,

3 个答案:

答案 0 :(得分:8)

使用其他人创建的格式化程序进行解析比解析一个你可以控制的格式化程序更棘手(Tunaki的parseDefaulting()答案是正确的。)但是可以这样做:

public static LocalDateTime getDate(String s, DateTimeFormatter format) {
    TemporalAccessor dt = parser.parseBest(
            str, LocalDateTime::from, LocalDate::from, LocalTime::from, YearMonth::from);

    if (dt instanceof LocalDate) {
        return ((LocalDate) dt).atStartOfDay();
    } else if (dt instanceof LocalTime) {
        return ((LocalTime) dt).atDate(LocalDate.now());
    } else if (dt instanceof YearMonth) {
        return ((YearMonth) dt).atDay(1).atStartOfDay();
    } else {
        return LocalDateTime.from(dt);
    }
}

我没有测试过上面的代码,但这是parseBest()方法设计的目的。

答案 1 :(得分:7)

我认为你有相反的问题。您不想确定格式化程序是仅使用日期还是日期/时间。您可以根据应解析的内容以及要将结果存储到的内容创建格式化程序。显然,如果你创建一个不处理时间部分的格式化程序,用它来解析成LocalDateTime是一种误解。

如果您需要使用两种格式(例如"yyyy-MM-dd""yyyy-MM-dd HH:mm:ss"(带有时间部分)来解析可以到达的日期,则无法通过解析来确定它应该执行的操作,但最多可以格式化程序在没有时间的情况下提供默认值。

使用Java Time,可以使用可选部分和默认值完成此操作。例如,模式"yyyy-MM-dd[ HH:mm:ss]"将能够解析日期字符串(如"2016-01-11")和日期/时间字符串(如"2016-01-11 20:10:10")。如果您将其存储到LocalDateTime,则需要在没有时间组件的情况下提供默认值。这是通过parseDefaulting(field, value)完成的:这将告诉格式化程序返回该chrono字段的给定默认值(如果尚未设置)。

以下代码创建了这样一个格式化程序,并将时间部分默认为午夜。

public static void main(String[] args) {
    DateTimeFormatter formatter = 
            new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd[ HH:mm:ss]")
                                          .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
                                          .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
                                          .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
                                          .toFormatter();

    LocalDateTime dt1 = LocalDateTime.parse("2016-01-11", formatter);
    LocalDateTime dt2 = LocalDateTime.parse("2016-01-11 20:10:10", formatter);
}

这个逻辑当然可以扩展到只解析String并将日期组件默认为当前日期。

答案 2 :(得分:3)

与标题中的主要问题相关,DateTimeFormatter仅返回常规TemporalAccessor,而不是具体所需的结果类型。相反,用户需要间接使用解析器的结果来提供具体类型的静态from(parsed) - 方法。因此,您将在这些方法的文档中找到成功解析所需的字段类型:

LocalDate requires EPOCH_DAY

LocalTime requires NANO_OF_DAY

因此,查询这些字段的原始解析数据以确定格式化程序是日期,时间还是组合就足够了。

DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.ENGLISH);
TemporalAccessor tacc = dateFormatter.parse("2015-07-24");
System.out.println("date-like: " + tacc.isSupported(ChronoField.EPOCH_DAY)); // true
System.out.println("time-like: " + tacc.isSupported(ChronoField.NANO_OF_DAY)); // false


DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm", Locale.ENGLISH);
tacc = timeFormatter.parse("17:45");
System.out.println("date-like: " + tacc.isSupported(ChronoField.EPOCH_DAY)); // false
System.out.println("time-like: " + tacc.isSupported(ChronoField.NANO_OF_DAY)); // true

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm", Locale.ENGLISH);
tacc = dateTimeFormatter.parse("2015-07-24 17:45");
System.out.println("date-like: " + tacc.isSupported(ChronoField.EPOCH_DAY)); // true
System.out.println("time-like: " + tacc.isSupported(ChronoField.NANO_OF_DAY)); // true

但是我必须同意@Tunaki的意见从设计的角度来看,使用字段的默认值是更好的主意。要完成上面给出的代码,如果输入与模式等不匹配,还需要特殊的异常处理。