问题
This question is similar to How to parse ZonedDateTime with default zone? but addinitional condition.
I have a string param that represent a date in UK format: "3/6/09". It doesn't contain time, only date. But may contain it and even time zone.
And I want to parse it to ZonedDateTime
.
public static ZonedDateTime parse(String value) {
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(SHORT).withLocale(Locale.UK).withZone(ZoneId.systemDefault());
TemporalAccessor temporalAccessor = formatter.parseBest(value, ZonedDateTime::from, LocalDateTime::from, LocalDate::from);
if (temporalAccessor instanceof ZonedDateTime) {
return ((ZonedDateTime) temporalAccessor);
}
if (temporalAccessor instanceof LocalDateTime) {
return ((LocalDateTime) temporalAccessor).atZone(ZoneId.systemDefault());
}
return ((LocalDate) temporalAccessor).atStartOfDay(ZoneId.systemDefault());
}
But, it fails with exception:
java.time.format.DateTimeParseException: Text '3/6/2009' could not be parsed at index 6
It's a bug for me, or isn't?
回答1:
In my opinion is not a bug. Your approach is flawed.
First of all you are returning a ZonedDateTime so it is expected that the String contains full date, time and zone information. The string "3/6/09" should be parsed to a LocalDate.
Second, you are delegating a runtime detection of format to the library. Again, you should be parsing/formatting an expected format. Your application should know wether is expecting a full date & time or a partial (only date or only time).
Anyway you will have more luck detecting the format and then using different parsing methods.
Only local date:
DateTimeFormatter
.ofLocalizedDate(FormatStyle.SHORT)
.parse(value, LocalDate::from)`
Zoned date and time:
DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT)
.parse(value, ZonedDateTime::from)`
回答2:
The format used can be seen using the getLocalizedDateTimePattern()
method:
String fmt = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.UK);
The result is "dd/MM/yy HH:mm"
.
As such, the format is expecting both a date and a time with a space separator, so that is what must be provided.
In addition, the format/parse expects there to be two digits for the day-of-month and two digits for the month-of-year. Thus, you would need to pass in "03/06/09 00:00" in order to get the result you expect, in which case you can parse directly to a LocalDateTime
.
Alternatively, use ofLocalizedDate()
:
DateTimeFormatter formatter =
DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT).withLocale(Locale.UK);
LocalDate date = LocalDate.parse("03/06/99", formatter);
Note that the input must still have two digits for the day and month.
Alternatively, parse using a specific pattern that can handle the missing leading zeroes:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("d/M/yy");
LocalDate date = LocalDate.parse("3/6/99", formatter);
LocalDate date = LocalDate.parse("03/06/99", formatter);
// handles both "3/6/99" and "03/06/99"
Update: Lenient parsing also handles this case:
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.parseLenient().appendPattern("dd/MM/yy").toFormatter();
LocalDate date = LocalDate.parse("3/6/99", formatter);
LocalDate date = LocalDate.parse("03/06/99", formatter);
// handles both "3/6/99" and "03/06/99"
来源:https://stackoverflow.com/questions/27488494/how-to-parse-string-with-date-but-without-time-in-local-format-to-zoneddatetime