p:datePicker(或p:calendar)上的最小和最大日期导致 ClassCastException 或最小日期必须小于最大日期

Min and maxdate on p:datePicker (or p:calendar) causes ClassCastException or minimum date must be less than maximum date

我正在使用 PrimeFaces 10.0.0。当使用固定 mindate.

p:datePicker 时,我需要将 maxdate 动态设置为 10 年后

要设置的实用程序 bean maxdate:

@Named @ApplicationScoped
public class Dates {

    public LocalDate getLocalDateNow() {
        return LocalDate.now();
    }
}

日期选择器:

<p:datePicker pattern="MM/dd/yyyy"
              mindate="1/1/2011"
              maxdate="#{dates.localDateNow.plusYears(10)}"
              .../>

引发此异常:

javax.servlet.ServletException: java.lang.String cannot be cast to java.time.chrono.ChronoLocalDate
    ...
Caused by: java.lang.ClassCastException: java.lang.String cannot be cast to java.time.chrono.ChronoLocalDate
    at java.time.LocalDate.compareTo(LocalDate.java:137)
    at org.primefaces.component.api.UICalendar.validateMinMax(UICalendar.java:375)
    at org.primefaces.component.calendar.BaseCalendarRenderer.encodeEnd(BaseCalendarRenderer.java:84)
    at org.primefaces.component.datepicker.DatePickerRenderer.encodeEnd(DatePickerRenderer.java:89)
    at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:619)
    ...

当我将实用程序 bean 更改为 return a String:

public String getLocalDateNow() {
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("M/d/uuuu");
    LocalDate localDateNow = LocalDate.now().plusYears(10);
    return localDateNow.format(dtf);
}

随后:

maxdate="#{dates.localDateNow}"

事情是这样的:

javax.servlet.ServletException: DatePicker : "form:startDate" minimum date must be less than maximum date.
    ...
Caused by: javax.faces.FacesException: DatePicker : "form:serviceStart" minimum date must be less than maximum date.
    at org.primefaces.component.api.UICalendar.validateMinMax(UICalendar.java:378)
    at org.primefaces.component.calendar.BaseCalendarRenderer.encodeEnd(BaseCalendarRenderer.java:84)
    at 
    ...

如何防止这些异常?

看看 source code of UICalendar.validateMinMax:

public void validateMinMax(FacesContext context) {
    Comparable minDate = (Comparable) getMindate();
    Comparable maxDate = (Comparable) getMaxdate();
    if (minDate != null && maxDate != null && maxDate.compareTo(minDate) < 0) {
        String id = getClientId(context);
        String component = this.getClass().getSimpleName();
        throw new FacesException(component + " : \"" + id + "\" minimum date must be less than maximum date.");
    }
}

它只是检查 Comparable,并不关心实际类型。如果您对一个日期使用 LocalDate 而对另一个日期使用 String,比较将导致错误(如您的第一个堆栈跟踪所示)。

对两者都使用 String 也不好,因为比较字符串与日期值无关。如果您使用像 yyyy-MM-dd 这样的日期模式,可能会更好,但最好不要使用字符串。

您应该为最小日期和最大日期使用相同的(非字符串)类型(例如 LocalDate),这样就可以了。

话虽如此,validateMinMax 的当前实施存在缺陷。这已被报告(并在撰写此答案时修复):

修复将在 PrimeFaces 11 中结束。

顺便说一下,此代码与 p:calendar 组件共享,因此同样的原则适用于那里。请注意 p:calendar 已弃用!