如何将句点移到比较的另一边?
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)
为假。
给定一个 LocalDate
值 d
和一个 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 + p
和 d
来检测这两者中的哪一个适用。
所以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 的假设是不必要的。
当涉及到 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)
为假。
给定一个 LocalDate
值 d
和一个 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 + p
和d
来检测这两者中的哪一个适用。所以
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 的假设是不必要的。