问题
I need to parse date-times as strings coming as two different formats:
- 19861221235959Z
- 1986-12-21T23:59:59Z
The following dateTimeFormatter pattern properly parses the first kind of date strings
DateTimeFormatter.ofPattern ("uuuuMMddHHmmss[,S][.S]X")
but fails on the second one as dashes, colons and T are not expected.
My attempt was to use optional sections as follows:
DateTimeFormatter.ofPattern ("uuuu[-]MM[-]dd['T']HH[:]mm[:]ss[,S][.S]X")
Unexpectedly, this parses the second kind of date strings (the one with dashes), but not the first kind, throwing a
java.time.format.DateTimeParseException: Text '19861221235959Z' could not be parsed at index 0
It's as if optional sections are not being evaluated as optional...
回答1:
As Peter stated in the comments, the problem is that your pattern is considering the entire string as the year. You can use .appendValue(ChronoField.YEAR, 4)
to limit it to four characters:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4)
.appendPattern("[-]MM[-]dd['T']HH[:]mm[:]ss[,S][.S]X")
.toFormatter();
This parses correctly with both of your examples.
If you fancy being even more verbose, you could do:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4)
.optionalStart().appendLiteral('-').optionalEnd()
.appendPattern("MM")
.optionalStart().appendLiteral('-').optionalEnd()
.appendPattern("dd")
.optionalStart().appendLiteral('T').optionalEnd()
.appendPattern("HH")
.optionalStart().appendLiteral(':').optionalEnd()
.appendPattern("mm")
.optionalStart().appendLiteral(':').optionalEnd()
.appendPattern("ss")
.optionalStart().appendPattern("X").optionalEnd()
.toFormatter();
回答2:
It’s not clear from the documentation, but my guess is that the following is what happens.
When you use uuuuMMddHHmmss
in your format pattern string, the formatter can easily see that there are several adjacent numeric fields and therefore uses the field widths to separate the fields. The first 4 digits are taken to mean the year, and so on.
When instead you use uuuu[-]MM[-]dd['T']HH[:]mm[:]ss
, the formatter doesn’t perceive it as adjacent numeric fields. I agree with the comments by Peter Lawrey that it therefore takes a longer run of digits for year and in the end overflows the maximum year (999999999) and throws the exception.
The solution? Please refer to Michael’s answer.
回答3:
DateTimeFormatter based on patterns are not smart enough to handle both an optional section and the possibility to have two numeric fields without separation. When you do need your numeric fields to be without separator, no question asked, then the pattern understands that the change of pattern letter from u to M means that it needs to count the digits to know which digit is part of which fields. But when this is not a certainty, then the pattern doesn't try that. It sees one numeric field described entirely and not immediately followed with another numeric fields. Therefore, there is no reason to count digits. All the digits are part of the field supposed to be represented here.
To do that, you shouldn't try to build your DateTimeFormatter with a pattern, but rather with a Builder. Get your inspiration from DateTimeFormatter.BASIC_ISO_DATE
and the others nearby.
回答4:
At first glance your second format should be working for both cases. Not sure why it doesn't. BTW I am curious why you used 'u' as opposed to 'y' for a year. So I would try using 'y' as well just to see if it makes a difference. But in general you are touching on the interesting point - how to parse a date from unknown format (imagine that instead of 2 possible formats you are dealing with unknown number of formats). I actually wrote once a parser like that. The idea that I used to solve this problem is described in my article Java 8 java.time package: parsing any string to date. You might find the idea useful. In short the idea is to have external file that holds all supported formats in it and try to apply each format one-by-one until one works.
来源:https://stackoverflow.com/questions/51176936/java-8-datetimeformatter-parsing-optional-sections