使用 p:calendar 相互限制开始和结束日期时间(无验证)
Posted
技术标签:
【中文标题】使用 p:calendar 相互限制开始和结束日期时间(无验证)【英文标题】:Mutually restricting begin and end date-times using p:calendar (no validation) 【发布时间】:2013-04-16 06:23:53 【问题描述】:我们需要向用户呈现两个 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
添加到每个p:calendar
的update
属性中,以便在更新期间调用各自的getter(getSelectedBeginDate()
和getSelectedEndDate
+ 最小/最大限制器)。
在更新上放置@this
但是会混淆 p:calendar 组件,使时间滑块只能滑动一次。随后的滑块事件将被简单地忽略,表现不佳。
问题
您通常如何解决这个问题? 是使用p:remoteCommand
的方式来实现我们想要的吗?
可选问:
为什么没有实施 PrimeFaces p:calendar 来提供单一的 minDateTime 和 maxDateTime,这可能会解决手头的问题?我敢打赌,我描述的这种情况之前已经解决了。如果您能描述您设法解决这个问题的方法(甚至分享部分解决方案),我将非常感激。
【问题讨论】:
回答您的可选问题:假设某人只能选择 8 点到 17 点(办公时间)之间的日期时间,尽管需要多天。您需要限制小时数而不是整个日期时间。 很好地解释了这个问题。 AFAIK Omnifaces 通过<o:validateOrder>
为您提供帮助。它甚至包括一个使用<p:calendar>
的示例:)。
前段时间遇到同样的问题,最后我选择了只控制 endDate 大于 beginDate 并在日期相等的情况下在 ManagedBean 中处理小时和分钟的验证。如果是这样,我正在向用户显示一个对话框弓,说 endDate> beginDate.
我建议您也在 PrimeFaces 论坛中发布您的问题。这听起来应该很容易完成。如果存在完整的日期对象(时间戳)(p:calendar),它应该可以通过 javascript 或 Java 以某种方式访问。问候,
处理p:calendar
s 时的一个重要观察是,您必须确保value="..."
、mindate="..."
、maxdate=""
等属性永远不会获得任何null 来自计算它们的 bean 的值。这严重混淆了现有组件(它通常回退到最后一个已知的非空值,看起来好像日历会缓存而不更新组件)。
【参考方案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 调用(不重定向,只发送标准回发等)中幸存下来,并且每个事件处理程序都在获取面部上下文(例如。FacesContext facesContext = FacesContext.getCurrentInstance();
)
d) mindate
之类的东西可能不会像你期望的那样工作,而且我不认为自动行为会这么容易被插入。
当这些选项不可用时,您必须自己动手:
哲学/用户体验: 我要做的第一件事是从这对日期中消除对安排或视角的期望。不要将该对视为在时间轴上暴露或期望方向的向量。
换句话说,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);
评估精度: 在使用任何语言处理一系列日期时,我会考虑的第二件事类似于处理浮点数的方式。对于比较,不要比较是否相等,而是将增量与“可接受的错误级别”进行比较。换句话说,应用程序实际上只关心一定程度的精度,因此请确保仅捕获和考虑该精度:
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);
强制精度: 第三,即使在分辨率范围内,我们如何解决值的差异? (Out 应用程序的精度超出了它可以处理、预期或需要的程度)。
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. 您可能有一些特定于浏览器/浏览器版本的问题会导致不良行为,并且像其他任何事情一样,存在已知和未知的问题。
【讨论】:
以上是关于使用 p:calendar 相互限制开始和结束日期时间(无验证)的主要内容,如果未能解决你的问题,请参考以下文章