Java 如何解析只有4位数的年份
我用乔达的时间来分析这些年:Java 如何解析只有4位数的年份,java,jodatime,date-parsing,Java,Jodatime,Date Parsing,我用乔达的时间来分析这些年: private DateTime attemptParse(String pattern, String date) { DateTimeFormatter parser = DateTimeFormat.forPattern(pattern).withLocale(Locale.ENGLISH); DateTime parsedDateTime = parser.parseLocalDateTime(date).toDateTime
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”、“yyy-MMM-dd”、“yyy-MMM-dd”、“yyy-MMM”等、“yyyy”
。一个坏了,我就试试下一个
当字符串实际上只有4位数字时(例如:“2016”
),它就像一个符咒。问题是我有时会收到这样的邮件:“201400”
。Joda Time将其与“yyyy”
模式匹配,并返回一个年份为201400
的日期
我想避免使用丑陋的if来检查
year>9999
。使用Joda Time有什么方法可以做到这一点吗?要解析多种格式,您可以创建大量的DateTimeParser
实例,并在一个格式化程序中加入所有实例(而不是一个接一个地尝试)
这将需要一个DateTimeFormatterBuilder
,它还将用于在输入中强制执行特定数量的数字(不幸的是,没有办法强制执行特定数量的数字,就像只使用DateTimeFormat.forPattern()
)
首先,创建大量org.joda.time.format.DateTimeParser
实例(每个可能的模式一个):
然后用所有这些模式创建一个数组,并用它创建一个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
可以解析Jan
和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
,例如:
java.lang.IllegalArgumentException:无效格式:“201400”在“00”处格式不正确
DateTimeFormatter
是不可变的且线程安全的,因此您不需要每次调用验证方法时都创建它。您可以在方法之外创建它(例如在静态final
字段中)
这比每次执行验证时创建一个格式化程序,并在发生异常时转到下一个格式化程序要好。创建的格式化程序已经在内部执行了该操作,将转到下一个模式,直到找到一个有效的模式为止(如果所有模式都失败,则抛出异常)
Java新的日期/时间API Joda Time处于维护模式,正在被新的API所取代,因此我不建议使用它启动新项目。甚至在文章中也提到:“请注意,Joda Time被认为是一个基本上“完成”的项目。没有计划进行重大的增强。如果使用Java SE 8,请迁移到Java.Time(JSR-310)。” 如果您不能(或不想)从Joda Time迁移到新API,可以忽略此部分 如果使用<强> java 8 < />,请考虑使用这更容易 如果您使用的是Java6或7,则可以使用,这是Java8新的日期/时间类的一个很好的后端口。对于Android,您还需要(更多关于如何使用它的信息) 下面的代码适用于这两种情况。 唯一的区别是包名(在Java8中是
Java.time
,而在ThreeTen Backport(或Android的ThreeTenABP)中是org.ThreeTen.bp
),但类和方法名是相同的
这个新API比以前的API严格得多,因此格式化程序只能使用精确的位数(请注意,有些类与Joda Time非常相似):
此代码适用于2014
,但适用于201400
或201
(或任何其他不含4位数字的值)时会引发异常:
java.time.format.DateTimeParseException:无法在索引0处分析文本“201400”
这样,验证代码就可以处理字符串数组。
只有一个细节:解析日期时,Joda Time会在输入没有某些字段时设置默认值(如月份变为一月,日期变为1,小时/分钟/秒设置为零,等等) 如果只是验证输入,则不需要返回任何内容。只要检查是否抛出异常,您就会知道输入是否有效 但是,如果您只需要年份值,可以使用: 如果要将年份值设置为
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
您可以选择您想要的任何值作为字段的默认值,并使用任何值。您要说的是Jodatime应该以某种方式猜测它应该将“201400”解析为2014。我认为这不在图书馆的范围之内。您应该自己预处理数据,例如使用:
String normalizedDate = String.format("%4s", date).trim();
为什么不只是解析<代码>子串(日期,0, 4)< /Cord>?@ Grffy狗狗,因为我不认为201400是一个有效的日期,所以我必须抛出一个异常。字符串的长度不是告诉你吗?这可能是比我的预处理答案更好的想法;实际上,我不想改为DateTimeFormatterBuilder,因为我简化了这个问题。我试图解析多种格式:“yyyy-MM-dd”、“yyy-MMM-dd”、“yyy-MMM-dd-dd”、“yyy-MMM”、(等)、“yyyy”。当无效日期“201400”出现时,它会被转换,我希望Jodatime抛出一个异常。如果我要切换到DateTimeFormatterBuilder,我需要使用它创建每个日期模式:/@micdcar在这种情况下,您可以编辑您的问题,以便我可以相应地更新我的答案吗?@Hugo谢谢您的回答,它确实解决了问题。霍
Year year = Year.parse("2014", parser);
int yearValue = year.getValue(); // 2014
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
String normalizedDate = String.format("%4s", date).trim();