如果找不到该字段,parseDefaulting将设置该字段的值,即使对于不在该模式中的字段也是如此,因此您最终可能会遇到解析结果中存在年份和年份的情况.
对我来说,最简单的解决方案是在评论中建议的:检查输入是否包含带有正则表达式的年份(或看起来像一个数字的东西,例如4位数),或检查输入的长度,然后创建格式化程序相应的(没有默认值).例子:
if (input_without_year) {
LocalDate d = MonthDay
.parse("12/06", DateTimeFormatter.ofPattern("MM/dd"))
.atYear(1970);
} else {
// use formatter with year, without default values
}
但如果你想要一个通用的解决方案,我担心它会更复杂.另一种方法是解析输入并检查其中是否有任何年份字段.如果没有,那么我们将其更改为返回年份的默认值:
public static TemporalAccessor parse(String pattern, String input) {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pattern, Locale.ROOT);
final TemporalAccessor parsed = fmt.parse(input);
// check year and year of era
boolean hasYear = parsed.isSupported(ChronoField.YEAR);
boolean hasYearEra = parsed.isSupported(ChronoField.YEAR_OF_ERA);
if (!hasYear && !hasYearEra) {
// parsed value doesn't have any year field
// return another TemporalAccessor with default value for year
// using year 1970 - change it to Year.now().getValue() for current year
return withYear(parsed, 1970); // see this method's code below
}
return parsed;
}
首先,我们解析并获取包含所有已解析字段的TemporalAccessor.然后我们检查它是否有年份或年份字段.如果它没有任何这些,我们创建另一个具有年份默认值的TemporalAccessor.
在上面的代码中,我使用1970,但您可以将其更改为您需要的任何内容. withYear方法有一些重要的细节需要注意:
>我假设输入总是有月和日.如果不是这种情况,您可以更改下面的代码以使用它们的默认值
>要检查字段是否存在,请使用isSupported方法
withYear方法如下:
public static TemporalAccessor withYear(TemporalAccessor t, long year) {
return new TemporalAccessor() {
@Override
public boolean isSupported(TemporalField field) {
// epoch day is used by LocalDate.from
if (field == ChronoField.YEAR_OF_ERA || field == ChronoField.EPOCH_DAY) {
return true;
} else {
return t.isSupported(field);
}
}
@Override
public long getLong(TemporalField field) {
if (field == ChronoField.YEAR_OF_ERA) {
return year;
// epoch day is used by LocalDate.from
} else if (field == ChronoField.EPOCH_DAY) {
// Assuming the input always have month and day
// If that's not the case, you can change the code to use default values as well,
// and use MonthDay.of(month, day)
return MonthDay.from(t).atYear((int) year).toEpochDay();
} else {
return t.getLong(field);
}
}
};
}
现在这个工作:
System.out.println(LocalDate.from(parse("MM/dd", "12/06"))); // 1970-12-06
System.out.println(LocalDate.from(parse("uuuu/MM/dd", "2018/12/06"))); // 2018-12-06
System.out.println(LocalDate.from(parse("yyyy/MM/dd", "2018/12/06"))); // 2018-12-06
但我仍然认为第一种解决方案更简单.
替代
假设您总是创建LocalDate,另一种方法是使用parseBest:
public static LocalDate parseLocalDate(String pattern, String input) {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pattern, Locale.ROOT);
// try to create a LocalDate first
// if not possible, try to create a MonthDay
TemporalAccessor parsed = fmt.parseBest(input, LocalDate::from, MonthDay::from);
LocalDate dt = null;
// check which type was created by the parser
if (parsed instanceof LocalDate) {
dt = (LocalDate) parsed;
} else if (parsed instanceof MonthDay) {
// using year 1970 - change it to Year.now().getValue() for current year
dt = ((MonthDay) parsed).atYear(1970);
} // else etc... - do as many checkings you need to handle all possible cases
return dt;
}
然后我检查返回的类型并采取相应的行动.您可以展开它以检查所需的类型,也可以编写自己的TemporalQuery来处理特定情况.
有了这个,所有案例也有效:
System.out.println(parseLocalDate("MM/dd", "12/06")); // 1970-12-06
System.out.println(parseLocalDate("uuuu/MM/dd", "2018/12/06")); // 2018-12-06
System.out.println(parseLocalDate("yyyy/MM/dd", "2018/12/06")); // 2018-12-06