DateTimeFormatterBuilder无法选择格式

时间:2017-08-30 10:10:09

标签: java jodatime datetime-format datetime-parsing

我已配置格式化程序:

public static final DateTimeFormatter DATE_FORMATTER = new DateTimeFormatterBuilder()
        .append(forPattern("yyyy-MM-dd"))
        .append(forPattern("MM/dd/yy"))
        .append(forPattern("MMM dd, yyyy"))
        .toFormatter();

并尝试解析字符串2017-08-29

LocalDate.parse(dt, DATE_FORMATTER).toDateTimeAtStartOfDay().toLocalDateTime()

我收到错误:

  

IllegalArgumentException:格式无效:“2017-08-29”太短

如果我将“yyyy-MM-dd”保留为构建器中唯一的格式,则会出现错误。

我是否滥用了API?我希望解析器在第一个格式失败的情况下尝试另一种格式。

1 个答案:

答案 0 :(得分:3)

当你使用append方法时,你正在创建一个格式化程序,它接受所有三种模式,一个接一个(所有三个都是必需的)。

如果您想接受三种格式中的任何一种(只有其中一种),则必须使用appendOptional代替:

DateTimeFormatter DATE_FORMATTER = new DateTimeFormatterBuilder()
    .appendOptional(DateTimeFormat.forPattern("yyyy-MM-dd").getParser())
    .appendOptional(DateTimeFormat.forPattern("MM/dd/yy").getParser())
    .appendOptional(DateTimeFormat.forPattern("MMM dd, yyyy").getParser())
    .toFormatter();

现在您可以解析以下三种格式中的任何一种:

System.out.println(LocalDate.parse("2017-08-29", DATE_FORMATTER).toDateTimeAtStartOfDay().toLocalDateTime());
System.out.println(LocalDate.parse("08/29/17", DATE_FORMATTER).toDateTimeAtStartOfDay().toLocalDateTime());
System.out.println(LocalDate.parse("Aug 29, 2017", DATE_FORMATTER).toDateTimeAtStartOfDay().toLocalDateTime());

以上所有输出:

  

2017-08-29T00:00:00.000

只需一个注释:第三个​​格式化程序使用月份短名称(MMM),上面的代码假定系统的默认语言环境是英语(当您创建格式化程序时,默认情况下,它使用与系统默认语言环境对应的语言。)

但即使在运行时也可以在不事先通知的情况下进行更改,因此最好在格式化程序中指定java.util.Locale

示例:如果月份名称始终为英语,则只需使用等效的语言环境:

DateTimeFormatter DATE_FORMATTER = new DateTimeFormatterBuilder()
    .appendOptional(DateTimeFormat.forPattern("yyyy-MM-dd").getParser())
    .appendOptional(DateTimeFormat.forPattern("MM/dd/yy").getParser())
    .appendOptional(DateTimeFormat.forPattern("MMM dd, yyyy").getParser())
    // use English locale
    .toFormatter().withLocale(Locale.ENGLISH);

只需将区域设置更改为最符合您需求的区域设置即可。 Check the javadoc了解更多详情。

正如评论中提醒的那样,您还可以创建一个解析器数组并在DateTimeFormatterBuilder中使用:

// array with all possible patterns
DateTimeParser[] parsers = new DateTimeParser[] {
    DateTimeFormat.forPattern("yyyy-MM-dd").getParser(),
    DateTimeFormat.forPattern("MM/dd/yy").getParser(),
    DateTimeFormat.forPattern("MMM dd, yyyy").getParser() };

DateTimeFormatter DATE_FORMATTER = new DateTimeFormatterBuilder()
    // use array of all possible parsers
    .append(null, parsers)
    // use English locale
    .toFormatter().withLocale(Locale.ENGLISH);

这与前一个工作方式相同。

Java新日期/时间API

Joda-Time处于维护模式,正在被新的API取代,因此我不建议使用它来启动新项目。即使在joda's website中它也说:“请注意,Joda-Time被认为是一个很大程度上”完成“的项目。没有计划大的增强。如果使用Java SE 8,请迁移到java.time(JSR) -310)。“即可。

如果您不能(或不想)从Joda-Time迁移到新API,则可以忽略此部分。

如果您使用的是 Java 8 ,请考虑使用new java.time API。它更容易,less bugged and less error-prone than the old APIs

如果您使用的是 Java< = 7 ,则可以使用ThreeTen Backport,这是Java 8新日期/时间类的绝佳后端。对于 Android ,您还需要ThreeTenABP(更多关于如何使用它here)。

以下代码适用于两者。 唯一的区别是包名称(在Java 8中为java.time,在ThreeTen Backport(或Android的ThreeTenABP)中为org.threeten.bp),但类和方法名称是相同的

在创建格式化程序并解析它时,API非常相似:

DateTimeFormatter DATE_FORMATTER = new DateTimeFormatterBuilder()
    .appendOptional(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
    .appendOptional(DateTimeFormatter.ofPattern("MM/dd/yy"))
    .appendOptional(DateTimeFormatter.ofPattern("MMM dd, yyyy"))
    // use English locale
    .toFormatter(Locale.ENGLISH);

System.out.println(LocalDate.parse("2017-08-29", DATE_FORMATTER).atStartOfDay());
System.out.println(LocalDate.parse("08/29/17", DATE_FORMATTER).atStartOfDay());
System.out.println(LocalDate.parse("Aug 29, 2017", DATE_FORMATTER).atStartOfDay());

以上所有内容都会创建一个LocalDateTime,其值与2017-08-29T00:00对应。

您还可以使用可选模式(由[]分隔):

DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("[yyyy-MM-dd][MM/dd/yy][MMM dd, yyyy]", Locale.ENGLISH);

这与上面的方法相同。