使用 p:calendar 相互限制开始和结束日期时间(无验证)

2022-09-01 03:23:35

我们需要向用户呈现两个 p:calendar 组件,分别表示一个开始日期和结束日期。两个日期时间都有日期、小时和分钟。PrimeFaces 具有完美的 、、 、 和可用的属性。mindatemaxdateminHourmaxHourminMinuteminMinute

现在的要求是:

不可能将开始日期时间设置为大于或等于结束日期时间。不可能将结束日期时间设置为小于或等于结束日期时间。

以下等式应成立:

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:日历组件无法知道这一点,现在

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 ) );
        }
    }
}

这要求我们添加每个属性,以便在更新期间调用相应的 getter(和 + 最小/最大限制器)。@thisupdatep:calendargetSelectedBeginDate()getSelectedEndDate

但是,在更新上放置 a 会混淆 p:calendar 组件,使时间滑块只能滑动一次。后续滑块事件将被忽略,行为已损坏。@this

  • 您通常如何解决这个问题?
  • 使用方法是否实现了我们想要的?p:remoteCommand

可选 Q

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

我敢打赌,我描述的这种情况之前已经解决了。如果您能描述一下您设法解决这个问题的方法(甚至分享部分解决方案),我将不胜感激。


答案 1

前言:

我不与JSF合作,但有一些事情可能会引导你回到你想去的地方:

a) 在标准日历中仅使用 dateTime 的日期部分时,请考虑使用:

someCalendar.set(Calendar.MILLISECOND, 0)

b)考虑使用joda-time,因为它似乎经常被推荐(这里这里许多其他地方)而不是标准库,以确保正确性,性能和在许多情况下的易用性。

c)确保您的Bean作用域在每次ajax调用中幸存下来(不重定向,仅发送标准回发等),并且每个事件处理程序都在获取面部上下文(例如。FacesContext facesContext = FacesContext.getCurrentInstance();)

d)之类的东西可能不会像你期望的那样工作,我也不期望自动行为可以如此容易地插入。mindate

当这些选项不可用时,您必须自己动手:

哲学/用户体验:我要做的第一件事是从日期对中消除对安排或视角的期望。不要将该对视为在时间轴上公开或期望方向的向量。

  • 换句话说,或 日期是否总是小于或早于 或 日期?否,从历史数据的查询或对尚未发生或已经发生的事件应用更正可以看出?startfromendto

    这种内涵很容易让用户混淆他们是“回到”还是“前进”(并且很容易让自己感到困惑)。相反,我会将一对日期与它们之间有一个时间段的日期视为公正而简单的日期,或者一个或一个声明一个间隔的日期,并根据任何随后选择的值推断它们在时间轴上的相对位置。通过这种方式,您可以遵守各自的固有要求,即日期永远不会相等,左边总是向左,右边总是向右。a pair of datesrangeperiod

我们无法推断“开始”或“从”的含义,但我们可以推断出一些意义和相对关系:在时间轴上,右,左和中间。注意:在执行任何计算或比较之前,请始终将日期解析为 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;

  1. 截断:return value - diff;

  2. 最近(带偏差):return value + (diff < dateTimeResolutionInMs/ 2) ? -1 * diff : dateTimeResolutionInMs - diff;

  3. 其他:还有许多其他策略可以将值缩小或扩展到首选分辨率或精度

补遗:至于让 post-backs/Ajax 调用返回一个视图,其中包含你对元素触发的事件所期望的值,如果前言中的注释没有让你得到任何帮助,并且你确信你的 bean 被正确注册和识别,你可能希望将这个问题分离到一个新问题。您可能遇到一些浏览器/浏览器版本特定的问题,这些问题会导致意外行为,并且像其他任何事情一样,存在已知和未知的问题。calendar


答案 2

推荐