2017-09-13 90 views
4

我使用乔达时间解析几年是这样的:如何解析只有4位数字的年份

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的日期。

我想避免丑陋的检查year > 9999。有没有办法使用Joda-Time来做到这一点?

+0

为什么不只是解析'substr(date,0,4)'? – GriffeyDog

+0

@GriffeyDog因为我不认为201400是有效的日期,所以我必须抛出异常。 – micdcar

+0

字符串的长度没有告诉你吗? –

回答

3

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

这将需要一个DateTimeFormatterBuilder,这也将被用来强制执行一个特定数量的数字输入(不幸的是,没有办法强制执行一个特定数量的数字,就像你只想使用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(因为你也在你的问题的代码中使用它)。此区域表示月份名称将以英文显示(因此MMM可以分析像JanSep这样的值)。有了这个,你可以解析输入:

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,如:

java.lang.IllegalArgumentException异常:无效的格式为: “201400” 在 “00”

DateTimeFormatter畸形是不可变的和线程安全的,所以每次调用验证方法时都不需要创建它。您可以在方法外创建它(例如在static final字段中)。

这比每次执行验证时创建一个格式化程序并在出现异常时转到下一个更好。创建的格式化程序已经在内部完成了,直到找到一个可用的模式(或者在所有模式失败时抛出异常)。


新的Java日期/时间API

乔达时间是在修的模式,正在被新的API所取代,所以我不建议开始使用它的新项目。即使在joda's website它说:“请注意,乔达时间被认为是一个很大程度上”完成“的项目。计划没有重大改进。如果使用Java SE 8,请迁移到java.time(JSR-310)。“

如果您不能(或不想)从Joda-Time迁移到新的API,那么您如果您使用的Java 可以忽略

这一部分,可以考虑使用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 java.time和ThreeTen反向移植(或Android的ThreeTenABP)为org.threeten.bp),但类和方法是相同的。

这个新的API是比以往更严格,所以格式化只与数字的确切人数(注意,有些课程是非常相似的乔达时间)工作原理:

// 4 digits in year 
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy", Locale.ENGLISH); 
fmt.parse("2014"); // OK 
fmt.parse("201400"); // exception 
fmt.parse("201"); // exception 

此代码与2014工作,但与201400201(或不正是4位其他值),它抛出一个异常:

java.time.format.DateTimeParseException:文本“201400”不能在索引0

解析

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


这里只有一个细节:解析为日期时,乔达时设置的默认值时,输入所没有的一些领域(如一个月变得月份,天变为1,时/分/秒的设置为零等)。

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

如果你只需要一年的值,不过,你可以使用Year class

DateTimeFormatter parser = DateTimeFormatter.ofPattern("yyyy", Locale.ENGLISH); 
System.out.println(Year.parse("2014", parser)); // ok 
System.out.println(Year.parse("201400", parser)); // exception 

如果你想在一年价值为int

Year year = Year.parse("2014", parser); 
int yearValue = year.getValue(); // 2014 

但是,如果你想要获取日期对象,您需要手动设置默认值 - 新API非常严格,并且不要自动设置这些值。在这种情况下,您必须使用DateTimeFormatterBuilder来设置默认值。

我还它解析为LocalDateTime,就像例如:

DateTimeFormatter fmt = new DateTimeFormatterBuilder() 
    // string pattern 
    .appendPattern("yyyy") 
    // default month is January 
    .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1) 
    // default day is 1 
    .parseDefaulting(ChronoField.DAY_OF_MONTH, 1) 
    // default hour is zero 
    .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) 
    // default minute is zero 
    .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) 
    // set locale 
    .toFormatter(Locale.ENGLISH); 
// create LocalDateTime 
System.out.println(LocalDateTime.parse("2014", fmt)); // 2014-01-01T00:00 
System.out.println(LocalDateTime.parse("201400", fmt)); // exception 

你可以选择你想要作为默认的字段的任何值,并且使用任何new available date types的。

+1

这可能是比我的预处理答案更好的主意;-) –

+0

其实我不想更改为DateTimeFormatterBuilder,因为我简化了问题。我试图解析多种格式:“yyyy-MM-dd”,“yyyy-MMM-dd”,“yyyy MMM dd-dd”,“yyyy MMM”,(等),“yyyy”。 而当无效日期“201400”显示它被转换,我想让Jodatime抛出一个异常。如果我要切换到DateTimeFormatterBuilder,我需要使用它来创建每个日期模式:/ – micdcar

+0

@micdcar在这种情况下,是否可以编辑您的问题,以便我可以相应更新我的答案? – 2017-09-13 14:16:24

0

你在说的是,Jodatime应该以某种方式猜测它应该将201400解析为2014.我认为这不在该库的合理范围内。你应该自己预处理数据,例如使用:

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

感谢您的回答。其实我不想转换这个日期,我想把201400视为错误。 – micdcar

相关问题