如何解析只有4位数的年份

时间:2017-09-13 14:01:05

标签: java jodatime date-parsing

我使用Joda-Time解析这样的年代:

private DateTime attemptParse(String pattern, String date) {
        DateTimeFormatter parser = DateTimeFormat.forPattern(pattern).withLocale(Locale.ENGLISH);
        DateTime parsedDateTime = parser.parseLocalDateTime(date).toDateTime(WET);
        return parsedDateTime;
    }

我尝试解析多种格式:"yyyy-MM-dd", "yyyy-MMM-dd","yyyy MMM dd-dd","yyyy MMM", (etc), "yyyy"。当一个人不工作时,我会尝试下一个。

当字符串确实只有4位数时(例如:"2016"),它就像魅力一样。问题是我有时会收到这样的信息:"201400"。 Joda-Time将此与"yyyy"模式匹配,并返回年份201400的日期。

我想避免丑陋的if if to check year > 9999。有没有办法用Joda-Time做到这一点?

2 个答案:

答案 0 :(得分:4)

要解析多种格式,您可以创建大量DateTimeParser个实例,并将所有实例连接在一个格式化程序中(而不是一个接一个地尝试)。

这将需要DateTimeFormatterBuilder,这也将用于在输入中强制执行特定数量的数字(遗憾的是,无法使用{{1}强制执行特定数量的数字})。

首先,您创建了许多DateTimeFormat.forPattern()个实例(每个可能的模式一个):

org.joda.time.format.DateTimeParser

然后,您创建一个包含所有这些模式的数组,并使用它创建// only yyyy DateTimeParser p1 = new DateTimeFormatterBuilder() // year with exactly 4 digits .appendYear(4, 4).toParser(); // yyyy-MM-dd DateTimeParser p2 = new DateTimeFormatterBuilder() // year with exactly 4 digits .appendYear(4, 4) // rest of the pattern .appendPattern("-MM-dd").toParser(); // yyyy MMM DateTimeParser p3 = new DateTimeFormatterBuilder() // year with exactly 4 digits .appendYear(4, 4) // rest of the pattern .appendPattern(" MMM").toParser();

DateTimeFormatter

我还使用了// create array with all the possible patterns DateTimeParser[] possiblePatterns = new DateTimeParser[] { p1, p2, p3 }; DateTimeFormatter parser = new DateTimeFormatterBuilder() // append all the possible patterns .append(null, possiblePatterns) // use the locale you want (in case of month names and other locale sensitive data) .toFormatter().withLocale(Locale.ENGLISH); (因为您还在问题的代码中使用它)。此区域设置表示月份名称将使用英语(因此Locale.ENGLISH可以解析MMMJan等值。有了这个,你可以解析输入:

Sep

年份为System.out.println(parser.parseLocalDateTime("2014")); // OK System.out.println(parser.parseLocalDateTime("201400")); // exception System.out.println(parser.parseLocalDateTime("2014-10-10")); // OK System.out.println(parser.parseLocalDateTime("201400-10-10")); // exception System.out.println(parser.parseLocalDateTime("2014 Jul")); // OK System.out.println(parser.parseLocalDateTime("201400 Jul")); // exception 时,代码正常。当它为2014时,它会抛出201400,例如:

  

java.lang.IllegalArgumentException:格式无效:“201400”格式错误为“00”

java.lang.IllegalArgumentException是不可变且线程安全的,因此每次调用验证方法时都不需要创建它。您可以在方法之外创建它(例如在DateTimeFormatter字段中)。

这比每次执行验证时创建一个格式化程序更好,并在发生异常时转到下一个格式化程序。创建的格式化程序已在内部执行,转到下一个模式,直到找到一个有效的格式化程序(如果所有模式都失败,则抛出异常)。

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

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

这个新API比以前的API严格得多,因此格式化程序只能使用确切的位数(请注意,某些类与Joda-Time非常相似):

org.threeten.bp

此代码适用于// 4 digits in year DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy", Locale.ENGLISH); fmt.parse("2014"); // OK fmt.parse("201400"); // exception fmt.parse("201"); // exception ,但2014201400(或任何其他没有4位数的值)会抛出异常:

  

java.time.format.DateTimeParseException:无法在索引0处解析文本“201400”

有了这个,您的验证代码可以使用字符串数组。

只有一个细节:当解析为日期时,Joda-Time在输入没有某些字段时设置默认值(例如月份变为1月,白天变为1,小时/分钟/秒被设置为零等等)。

如果您只是验证输入,那么您不需要返回任何内容。只需检查是否抛出异常,您就会知道输入是否有效。

如果您只需要年份值,则可以使用Year class

201

如果您希望年份值为DateTimeFormatter parser = DateTimeFormatter.ofPattern("yyyy", Locale.ENGLISH); System.out.println(Year.parse("2014", parser)); // ok System.out.println(Year.parse("201400", parser)); // exception

int

但是如果你想获得一个日期对象,你需要手动设置默认值 - 新API非常严格,不要自动设置这些值。在这种情况下,您必须使用Year year = Year.parse("2014", parser); int yearValue = year.getValue(); // 2014

设置默认值

我还将其解析为DateTimeFormatterBuilder,例如:

LocalDateTime

您可以选择所需的任何值作为字段的默认值,并使用new available date types中的任何一个。

答案 1 :(得分:0)

你所说的是Jodatime应该以某种方式猜测它应该在2014年解析“201400”。我认为这不合理地属于该库的范围。您应该自己预处理数据,例如使用:

String normalizedDate = String.format("%4s", date).trim();