如何使用DateTimeFormatter在java8中处理yyyy-mm和yyyy
我使用SimpleDataFormat格式化或验证日期,但我希望通过使用Java8DateTimeFormatter使其线程安全。我很难达到某些要求 我的应用程序只接受三种格式。 “yyyy-MM-dd”、“yyyy-MM”、“yyyy” 以下是输入和预期输出:如何使用DateTimeFormatter在java8中处理yyyy-mm和yyyy,java,date,java-8,date-parsing,java-time,Java,Date,Java 8,Date Parsing,Java Time,我使用SimpleDataFormat格式化或验证日期,但我希望通过使用Java8DateTimeFormatter使其线程安全。我很难达到某些要求 我的应用程序只接受三种格式。 “yyyy-MM-dd”、“yyyy-MM”、“yyyy” 以下是输入和预期输出: input expected '2018-03-19' '2018-03-19' '2018-03' '2018-03' '2018' '2018' '2
input expected
'2018-03-19' '2018-03-19'
'2018-03' '2018-03'
'2018' '2018'
'2017-02-54' '2017-02'
'2016-13-19' '2016'
SimpleDataFormat
多年前,与最早版本的Java捆绑在一起的糟糕的日期时间类被中定义的现代Java.time类所取代
使用java 8 DateTimeFormatter实现线程安全
是的,与传统的日期时间类不同,java.time类使用并且是经过设计的
以下是输入和预期输出:
input expected
'2018-03-19' '2018-03-19'
'2018-03' '2018-03'
'2018' '2018'
'2017-02-54' '2017-02'
'2016-13-19' '2016'
您的一些输入可以通过其长度来检测
// Ten-digits long, assume ISO 8601 date.
LocalDate ld = LocalDate.parse( "2018-03-19" ) ;
// Seven digits long, assume ISO 8601 year-month.
YearMonth ym = YearMonth.parse( "2018-03" ) ;
// Four digits, assume year.
Year y = Year.parse( "2018" ) ;
请注意,上述输入都符合。在解析/生成字符串时,java.time类默认使用ISO 8601格式。因此,无需指定格式化模式。因此不需要显式的DateTimeFormatter
对象
“2017-02-54”“2017-02”
这个例子使我困惑。如果你的意思是“当遇到一个月的某一天无效时,只需使用年和月,而忽略该天”,我想你可以这样做。查看DateTimeFormatter
上的“宽松”模式。也许可以使用DateTimeFormatterBuilder
构建灵活的DateTimeFormatter
。但坦率地说,我会拒绝这些数据作为错误的输入。产生可靠的数据应该是数据发布者的工作,而不是消费者猜测错误数据背后的意图的工作
预期投入
“2016-13-19”“2016”
同样,在一个我不会玩的危险游戏中,试图猜测无效输入的有效部分。如果月份和日期无效,您如何知道年份有效?更糟糕的是,如果这些数据的发布者可以发布这样的错误数据,您如何知道表面上有效的2018-03-19
输入实际上是正确的?如果月份13
是错误的,您如何知道月份为03
的输入也不是错误
向出版商传授有关标准的这些有问题的数据,并要求他们修复错误。就像另一个答案中的Basil Bourque一样,我不一定相信你所要求的也是最适合你的。无论如何,作为这个好答案的一个小补充,我想提出一种方法来处理最后两个案例,无效日期,就像你说的那样 为了验证格式,我首先解析字符串,而不验证数字。这将拒绝三种格式中任何一种格式的字符串,例如
2016-03-19T12:00
或2016和一些无意义的字符串。DateTimeFormatter.parseUnresolved
方法负责这部分。如果出现解析错误,此方法在传递给它的ParsePosition
对象中设置错误索引,并返回null
(因此不会引发任何异常)。因此,我检查是否返回了null
private static DateTimeFormatter yearMonthFormatter = DateTimeFormatter.ofPattern("uuuu-MM")
.withResolverStyle(ResolverStyle.STRICT);
private static DateTimeFormatter yearFormatter = DateTimeFormatter.ofPattern("uuuu")
.withResolverStyle(ResolverStyle.STRICT);
public static Temporal parse(String input) {
// First try the three formats, uuuu-MM-dd, uuuu-MM, uuuu, in turn without resolving
TemporalAccessor parsed = null;
for (DateTimeFormatter formatter : Arrays.asList(DateTimeFormatter.ISO_LOCAL_DATE,
yearMonthFormatter, yearFormatter)) {
ParsePosition position = new ParsePosition(0);
TemporalAccessor parseAttempt = formatter.parseUnresolved(input, position);
if (parseAttempt != null && position.getIndex() == input.length()) {
// Success; exit loop
parsed = parseAttempt;
break;
}
}
if (parsed == null) { // didn’t match any of the three formats
throw new IllegalArgumentException("Invalid format: " + input);
}
// Try resolving to either LocalDate, YearMonth or Year
try {
return LocalDate.of(parsed.get(ChronoField.YEAR),
parsed.get(ChronoField.MONTH_OF_YEAR),
parsed.get(ChronoField.DAY_OF_MONTH));
} catch (DateTimeException dteRLd) {
try {
return YearMonth.of(parsed.get(ChronoField.YEAR),
parsed.get(ChronoField.MONTH_OF_YEAR));
} catch (DateTimeException dteRYm) {
return Year.of(parsed.get(ChronoField.YEAR));
}
}
}
让我们试试你的例子:
String[] inputs = {
"2018-03-19",
"2018-03",
"2018",
"2017-02-54",
"2016-13-19"
};
for (String input : inputs) {
Temporal parsed = parse(input);
System.out.format("%-10s %-10s %s%n", input, parsed, parsed.getClass().getName());
}
输出为:
我不明白为什么您有这些特殊的需求,也不明白您发布的旧代码如何产生您想要的结果。如果你解释得更深入一些,我们可能会提出更好的建议。在任何情况下,我们都祝贺您决定使用java.time,这是一种现代的java日期和时间API。
2018-03-19 2018-03-19 java.time.LocalDate
2018-03 2018-03 java.time.YearMonth
2018 2018 java.time.Year
2017-02-54 2017-02 java.time.YearMonth
2016-13-19 2016 java.time.Year