使用p:calendar相互限制开始和结束date时间(不validation)

我们需要向用户呈现两个p:日历组件,分别代表开始date和结束date。 两个date都有date,小时和分钟。 PrimeFaces具有完善的mindatemaxdateminHourmaxHourminMinuteminMinute属性。

现在的要求是:

将开始date时间设置为大于或等于结束date时间是不可能的。 将结束date时间设置为小于或等于结束date时间是不可能的。

下面的公式应该是真实的:

 begin datetime < end datetime 

现在我们尝试了以下JSF:

 <p:calendar id="begin-date" value="#{debugManager.selectedBeginDate}" mindate="#{debugManager.minBeginDate}" maxdate="#{debugManager.maxBeginDate}" maxHour="#{debugManager.maxBeginHour}" maxMinute="#{debugManager.maxBeginMinute}" pattern="yyyy-MM-dd HH:mm" showButtonPanel="true" readonlyInput="true" navigator="true" showOn="button" required="true"> <p:ajax event="dateSelect" update="end-date" /> </p:calendar> <p:calendar id="end-date" value="#{debugManager.selectedEndDate}" mindate="#{debugManager.minEndDate}" minHour="#{debugManager.minEndHour}" minMinute="#{debugManager.minEndMinute}" pattern="yyyy-MM-dd HH:mm" showButtonPanel="true" readonlyInput="true" navigator="true" showOn="button"> <p:ajax event="dateSelect" update="begin-date" /> </p:calendar> 

下面是一个示例性的最小/最大方法(logging结束date):

 public Date getMinEndDate() { return this.getSelectedBeginDate(); } 

正如你所看到的,最小结束date是当前AJAXselect的开始date。 正确设置结束date不允许设置结束date之后的开始date。

问题开始涉及到时间的方程式…

由于p:calendar的接口有独立的方法,bean必须提供逻辑:

 public int getMinEndHour() { Date selectedBeginDate = this.getSelectedBeginDate(); Date selectedEndDate = this.getSelectedEndDate(); if ( selectedBeginDate != null && DateUtil.isSameDay( selectedBeginDate, selectedEndDate ) ) { return DateUtil.getHourOf( selectedBeginDate ); } return ComplianceConstants.DEFAULT_COMPLIANCE_CASE_MIN_END_HOUR; } 

这基本上只是说如果开始date已经被设置,并且开始date和结束date目前是相同的,那么将可选结束时间(结束date的小时数)限制到开始时间。

操作:

 Set the begin datetime to 2013-04-20 12:34 (legit) Set the end datetime to 2013-04-22 00:00 (legit) 

现在结束date的时间是00:00,只要结束时间调整到12:35,就应该允许select2013-04-20的日历date。

然而,p:日历组件现在还不知道这一点

 sets the end datetime to 2013-04-20 00:00 (legit, but false) 

现在的问题是,当用户在日历中按下某个新的结束date时,mindate / maxdate属性不能限制用户点击与开始date相同。 如果结束date时间恰好在相同的开始date之前,那么我们无法做到这一点(这是错误的)。

现在的后续问题是用户能够closures日历,只需按下提交button即可将错误的数据插入到数据库中。 当然,validation者可以/应该运行,但我们必须以某种方式实现这个没有validation

我们接下来要做的是修补setSelectedBeginDate( Date selectedBeginDate )setSelectedEndDate( Date selectedEndDate )方法,如果date在同一天,则调整set java.util.Date时间部分。 像这样的东西:

 public void adjustSelectedEndDate() { if ( this.selectedEndDate != null ) { this.log.infov( "adjustSelectedEndDate: b-hour = {0}, e-hour = {1}", DateUtil.getHourOf( this.selectedBeginDate ), DateUtil.getHourOf( this.selectedEndDate ) ); if ( DateUtil.isSameDay( this.selectedBeginDate, this.selectedEndDate ) && ( DateUtil.getHourOf( this.selectedEndDate ) < DateUtil.getHourOf( this.selectedBeginDate ) ) || DateUtil.getHourOf( this.selectedEndDate ) == DateUtil.getHourOf( this.selectedBeginDate ) && DateUtil.getMinuteOf( this.selectedEndDate ) <= DateUtil.getMinuteOf( this.selectedBeginDate ) ) { this.log.info( "Adjusting selected end date!" ); this.selectedEndDate = DateUtil.addOneMinuteTo( DateUtil.copyTime( this.selectedBeginDate, this.selectedEndDate ) ); } } } 

这要求我们将@this添加到每个p:calendarupdate属性中,以便在更新期间调用相应的getters( getSelectedBeginDate()getSelectedEndDate +最小/最大限制器)。

在更新上放置一个@this会使p:calendar组件混淆,使时间滑块只能滑动一次。 后面的滑块事件被忽略,performance破碎。

Q的

  • 你通常如何解决这个问题?
  • 是用p:remoteCommand的方式来实现我们想要的吗?

可选问

  • 为什么没有实现PrimeFaces p:calendar来提供单个minDateTime和maxDateTime,这可能可以解决手头的问题?

我敢打赌,我描述的这个情景之前已经解决了。 如果您能描述您设法解决这个问题的方法(或者甚至分享部分解决scheme),我将非常感激。

前言:

我不使用JSF,但有几件事情可能会让你回到你想要的地方:

a)在标准日历中只处理dateTime的date部分时 ,请考虑使用:

 someCalendar.set(Calendar.MILLISECOND, 0) 

b)在许多情况下,考虑使用joda时间 ,因为它似乎经常被推荐( 在这里 , 在这里 ,以及许多其他地方 )超过标准库的正确性,性能和易用性。

c)确保你的bean范围在每个ajax调用中生存(不redirect,只发送标准的FacesContext facesContext = FacesContext.getCurrentInstance();等),并且每个事件处理器获取faces上下文(例如FacesContext facesContext = FacesContext.getCurrentInstance();

d) mindate和类似的东西可能不像你所期望的那样工作,而且我也不认为自动行为可以相当容易插入。

当这些选项不可用时,你必须自己做所有事情:

Philisophical / UX:我要做的第一件事就是从这对date中去掉安排或观点的期望。 不要将这一对作为暴露或期望时间轴上的方向的向量。

  • 换句话说,是start还是from startdate总是小于或早于end或到目前为止? 不,如同历史数据查询所能看到的那样,或者对于还没有发生或者已经发生的事件进行修正。

    这个内涵很容易混淆用户,他们是“回到”还是“前进”(并且容易混淆自己)。 相反,我会把它们之间的时间段作为a pair of dates ,简单地说就是a pair of dates或一个range或一段period来声明一个时间间隔,并根据任何因此select的值推断它们在时间轴上的相对位置。 通过这种方式,您可以尊重date不相等的各自的和固有的要求,左边总是在左边,右边总是在右边。

我们无法推断出“开始”或“从何而来”的意思,但我们可以推断出一些意义和相对关系:按时间顺序的时间轴上的右,左和中间。 注意:在进行任何计算或比较之前,务必将dateparsing为UTC。

 long oneDateValue = oneDate.toUtc().toMilliseconds(); long anotherDateValue = anotherDate.toUtc().toMilliseconds(); long right = max (oneDateValue, anotherDateValue); long left = min (oneDateValue, anotherDateValue); 

评估精度:在处理任何语言的一系列date时,我会考虑的第二件事与处理浮点数的方式类似。 为了比较,不要比较平等,而是将增量与“可接受的错误级别”进行比较。 换句话说,应用程序实际上只关心一定程度的精确度,所以请确保仅捕获并考虑精度:

 const int dateTimeResolutionInMs = 86400000; // milliseconds per day public bool areEssentiallySame(long left, long right) { // the difference between right and left is less than our precision // requires, thus dates are effectively the same return (right - left < dateTimeResolutionInMs); } 

胁迫精度:第三,即使在分辨率的范围内,我们如何解决数值的差异? (外部应用程序比它能够处理或预期或需要的更精确。

long diff = value % dateTimeResolutionInMs;

  1. 截断: return value - diff;

  2. 最近(w / bias): return value + (diff < dateTimeResolutionInMs/ 2) ? -1 * diff : dateTimeResolutionInMs - diff; return value + (diff < dateTimeResolutionInMs/ 2) ? -1 * diff : dateTimeResolutionInMs - diff;

  3. 其他:还有很多其他策略可以缩减或扩大价值,以达到首选的分辨率或精度

附录:就后期/ Ajax调用返回包含calendar元素触发事件的值的视图而言,如果前言中的注释没有提供,那么您可能希望将这个问题分解为一个新的问题,不pipe你到底在哪里,你都知道你的bean已被正确注册和识别。 您可能会遇到一些浏览器/浏览器版本的具体问题,这些问题会导致不良的行为,并且与其他任何情况一样,存在已知和未知的问题。