如何将句点移到比较的另一边?

How do I move a Period to the other side of a comparison?

当涉及到 Period 时,- 不应该是 + 的倒数:将 3 月 31 日加一个月得到 4 月 30 日。从 4 月减去一个月30 产生 3 月 30 日,而不是 3 月 31 日。

我对涉及句点的数据库记录进行了筛选:

priorDate + period < today

这里,priorDate是从一个数据库列中导出的,period可以由用户配置,我需要找到所有满足条件的记录。

如果期间不涉及月或年,我可以将其转化为

priorDate < today - period

这允许将比较从客户端移动到数据库:它允许我避免检索所有记录只是为了丢弃不符合条件的记录。

如果经期涉及数月或数年,我该怎么做?

我可以假定公历,并且 period 是非负数。 (具体来说:我可以假设 priorDate 的所有值都为 priorDate + period >= priorDate,但如果可能的话,我想暂时不排除句点 "one month minus one day"。)

如果我有一个月的时间,今天是 4 月 30 日,那么我想弄清楚表达式应该变成 priorDate < new LocalDate(2018, 3, 30),这很容易:就是 today - period产生。

如果我有一个月的周期,今天是 2018 年 3 月 30 日,那么 today - period 将产生 2 月 28 日,但表达式应该变成与 3 月 1 日的比较:如果 priorDate 正好是 new LocalDate(2018, 2, 28),则 priorDate + period < new LocalDate(2018, 3, 30) 为真,而 priorDate < new LocalDate(2018, 2, 28) 为假。

给定一个 LocalDated 和一个 Period p:

如果p只包括月份或年份:

  • 如果天真的加法或减法 p 会产生无效日期,则结果会减少到月底。结果日期永远不会是 "rounded up" 到下个月。它的 year/month 组件将精确增加 p 中指定的数量。因此:

    • d - p 将产生最低的 x 使得 x + p == d,如果有这样的 x.
      在这种情况下,v + p < d 等同于 v < x.
    • 否则,d - p 将产生最大的 x,使得 x + p < d.
      在这种情况下,v + p < d 相当于 v <= x,或 v < x + Period.FromDays(1)

    可以通过比较 d - p + pd 来检测这两者中的哪一个适用。

    所以priorDate + period < refDate等同于priorDate < F(period, refDate),其中F定义为。

    LocalDate F(Period period, LocalDate refDate)
    {
      var result = refDate - period;
      if (result + period != refDate)
        result += Period.FromDays(1);
      return result;
    }
    

如果p包括days/weeks和months/years:

  • 加减p将首先加减month/year分量,然后是day/week分量。将句点移至比较的另一侧应首先减去或添加 day/week 分量,最后减去或添加 month/year 分量。上面的 F 不适用于例如priorDate == new LocalDate(2000, 1, 30)period == Period.FromMonths(1) + Period.FromDays(1)refDate == new LocalDate(2000, 3, 1):此处,priorDate + period == refDate(因为首先添加一个月以生成 2 月 29 日,然后添加一天以生成 3 月 1 日)但是 priorDate < F(period, refDate)(因为先减去一个月得到 2 月 1 日,然后减去一天得到 1 月 31 日)。为此,重要的是先减去天分量 ,这与 Period 算术通常的工作方式完全相反。

    所以priorDate + period < refDate等同于priorDate < G(period, refDate),其中G定义为。

    LocalDate G(Period period, LocalDate refDate)
    {
        var result =
            refDate
            - new PeriodBuilder {Weeks = period.Weeks, Days = period.Days}.Build()
            - new PeriodBuilder {Years = period.Years, Months = period.Months}.Build();
        if (result + period != refDate)
            result += Period.FromDays(1);
        return result;
    }
    

    注:new PeriodBuilder {Years = period.Years, Months = period.Months}.Build()的减法是先减年,后减月。此顺序必须 而不是 颠倒,除非添加另一个修正。否则会失败的测试用例是 d1 == new LocalDate(2000, 2, 29)p == Period.FromYears(1) + Period.FromMonths(1)d2 == new LocalDate(2001, 3, 31)。从 d2 中减去一个月得到 2001 年 2 月 28 日,然后减去一年得到 2000 年 2 月 28 日,加上一天得到 2000 年 2 月 29 日。先从 d2 中减去一年得到 2000 年 3 月 31 日,然后减去一个月产生 Feb 29 2000,加上一天产生 Mar 1 2000,这是正确的结果。

看来我的问题中 period 是 non-negative 的假设是不必要的。