我们要求向用户提供两个p:calendar组件,每个组件代表一个开始和结束日期。两个日期时间都有日期,小时和分钟。
PrimeFaces拥有完善的mindate
,maxdate
,minHour
,maxHour
,minMinute
和minMinute
属性。
现在的要求是:
无法将开始日期时间设置为大于或等于结束日期时间。 无法将结束日期时间设置为小于或等于结束日期时间。
以下等式应该成立:
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>
这是一个示例性的最小/最大方法(结束日期):
public Date getMinEndDate()
{
return this.getSelectedBeginDate();
}
如您所见,最小结束日期是当前AJAX选择的开始日期。正确设置结束日期不允许将开始日期设置为超过结束日期。
当将时间纳入等式时问题就开始了......
由于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;
}
这基本上只表示如果设置了开始日期并且开始日期和结束日期当前相同,则将可选结束时间(结束日期的minHour
)限制为开始时间。
操作:
Set the begin datetime to 2013-04-20 12:34 (legit)
Set the end datetime to 2013-04-22 00:00 (legit)
现在结束日期的时间是00:00,只要结束时间调整到至少12:35,就应该允许选择日历日期2013-04-20。
然而,p:calendar组件无法知道这一点,现在
sets the end datetime to 2013-04-20 00:00 (legit, but false)
...
现在的问题是,当用户在日历中按下某个新的结束日期时,mindate / maxdate属性不能限制用户点击与开始日期相同的内容。如果结束日期时间现在恰好位于相同的开始日期之前,那么我们无能为力(这是错误的)。
现在的后续问题是用户可以关闭日历,只需按下提交按钮即可将错误数据插入数据库。当然,可以/应该运行验证器,但我们必须以某种方式在没有验证器的情况下实现此目的。
我们接下来尝试的是修补setSelectedBeginDate( Date selectedBeginDate )
和setSelectedEndDate( Date selectedEndDate )
方法,以便在日期在同一天调整设置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
添加到每个update
的{{1}}属性中,以便相应的获取者(p:calendar
和getSelectedBeginDate()
+最小/最大限制器)将在更新期间调用。
然后在更新上放置getSelectedEndDate
会混淆p:calendar组件,使时间滑块只能滑动一次。简单地忽略后续滑块事件,表现为破碎。
问的
@this
实现我们想要的方式吗?可选Q :
我敢打赌,我所描述的这种情况以前已经解决了。如果您能描述一下您设法解决这个问题的方法(甚至可以共享部分解决方案),我将非常感激。
答案 0 :(得分:1)
<强>前言强>
我不使用JSF,但有一些东西可能会引导你回到你想要的地方:
a)working with just the date portion of a dateTime in a standard calendar时,请考虑使用:
someCalendar.set(Calendar.MILLISECOND, 0)
b)考虑使用joda-time,因为对于正确性,性能和标准库,似乎经常建议(here,here和many other places)在许多情况下易于使用。
c)确保你的bean范围在每个ajax调用中存活(不重定向,只发送标准的post-backs等),并且每个事件处理程序都获得了faces上下文(例如FacesContext facesContext = FacesContext.getCurrentInstance();
)
d)mindate
等可能不会像你期望的那样工作,我不认为自动行为可以很容易插入。
当这些选项不可用时,您必须自己完成所有操作:
Philisophical / UX: 我要做的第一件事就是从这对日期中删除对安排或观点的期望。不要将该对视为在时间线上暴露或期望方向的向量。
换句话说,start
或from
日期是否始终小于或早于end
或to
日期?不,可以看到历史数据的查询,或者对尚未发生或已经发生的事件应用更正?
这个含义很容易让用户混淆他们是“回到”还是“向前”(并且很容易让自己迷惑)。相反,我会将一对日期与它们之间的时间段视为公正而简单地a pair of dates
或range
或period
来宣布一个区间,并推断它们的相对位置。时间线取决于任何因此选择的值。通过这种方式,您可以遵守日期永远不相等的相应和固有要求,左侧始终位于左侧,右侧始终位于右侧。
我们无法推断'开始'或'来自'意味着什么,但我们可以推断出一些意义和相对关系:一个权利,一个左,以及一个按时间顺序排列的时间轴。 注意:在进行任何计算或比较之前,请始终将日期解析为UTC。
long oneDateValue = oneDate.toUtc().toMilliseconds();
long anotherDateValue = anotherDate.toUtc().toMilliseconds();
long right = max (oneDateValue, anotherDateValue);
long left = min (oneDateValue, anotherDateValue);
评估精度: 在使用任何语言处理一系列日期时,我会看到的第二件事与您如何处理浮点数类似。对于比较,不要比较相等性,而是将delta与“可接受的错误级别”进行比较。换句话说,应用程序实际上只关注一定程度的精度,因此请确保仅捕获并考虑该精度:
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;
截断:return value - diff;
最近(有偏见):return value + (diff < dateTimeResolutionInMs/ 2) ? -1 * diff : dateTimeResolutionInMs - diff;
其他:有很多其他策略可以将值缩小或扩展为首选分辨率或精度
附录:
至于获取post-backs / Ajax调用以返回具有您对calendar
元素触发的事件所期望的值的视图,如果前言中的注释,您可能希望将该问题与新问题分开。并没有让你到任何地方,而你know for certain your bean is properly registered and recognized.你可能有一些浏览器/浏览器版本的特定问题导致了不良行为,就像其他任何事情一样,存在已知和未知的问题。