如何使用 Joda Time 计算事件发生次数?

How to calculate number of event occurrences with Joda Time?

给定一个示例事件重现计划,其定义如下:

StartTime: Monday, 2016-June-06 09:00H
EndTime:   Monday, 2016-August-29 10:00H 
Period:    Bi-weekly

这意味着,每 2 周的星期一 09:00H,直到 2016 年 8 月 29 日。但是结束时间是 8 月 29 日星期一,也就是新一周的开始,但应该是包含在时间表中(因为它在 10:00H 结束)。所以我预计该事件会发生 7 次。

相反,如果结束时间是 8 月 29 日 08:00H,那么我预计该事件只会发生 6 次。

现在,我使用 Joda Time 计算出现次数: Weeks.weeksBetween(startTime, endTime).dividedBy(multiplier).getWeeks()

当然,这只给了我 6 次而不是 7 次,因为持续时间实际上只有 12 周,最后一次出现是在上周之后的星期一(星期日结束)。所以这不算整周。这同样适用于任何其他类型的时间表 - 月、日、年等。

我怎样才能可靠地计算两次之间事件的发生次数?

谢谢

使用伪代码,因为我对 Yoda 不太熟悉我是:

  1. counter = 1, someDate = 开始时间, increment = 两周
  2. 循环: someDate = someDate + increment
  3. if someDate < 结束时间: then counter++ else break loop

或者,对于不喜欢循环的人:

  1. 计算开始日期和结束日期之间的确切小时数
  2. 算出 24 * 7 * 2 适合上述小时数的频率

使用 JodaTime 库的示例代码:

int multiple = 2; //or whatever periodicity is needed
ReadablePeriod jodaPeriod;
switch (mPeriodType){
    case DAY:
        jodaPeriod = Days.days(multiple);
        break;
    case WEEK:
        jodaPeriod = Weeks.weeks(multiple);
        break;
    case MONTH:
        jodaPeriod = Months.months(multiple);
        break;
    case YEAR:
        jodaPeriod = Years.years(multiple);
        break;
    default:
        jodaPeriod = Months.months(multiple);
}
int count = 0;
LocalDateTime startTime = new LocalDateTime(mPeriodStart.getTime());
while (startTime.toDateTime().getMillis() < mPeriodEnd.getTime()){
    ++count;
    startTime = startTime.plus(jodaPeriod);
}
return count;

刚刚在 Joda 关注 GhostCat。通过从日期时间中删除 "H" 来采取一些快捷方式。我相信它可以解决。谢谢 Ghostcat :)

import java.util.Locale;

import org.joda.time.DateTime;
import org.joda.time.Hours;
import org.joda.time.Period;
import org.joda.time.Weeks;
import org.joda.time.format.DateTimeFormat;

public class TestDateJoda {
    public static void main(String[] args) {

        String startTime = "Monday, 2016-June-06 09:00";
        String endTime = "Monday, 2016-August-29 10:00";
        DateTime startDateTime = DateTimeFormat.forPattern("E, yyyy-MMM-dd HH:mm").withLocale(Locale.ENGLISH)
                .parseDateTime(startTime);
        DateTime endDateTime = DateTimeFormat.forPattern("E, yyyy-MMM-dd HH:mm").withLocale(Locale.ENGLISH)
                .parseDateTime(endTime);

        System.out.println(startDateTime);
        System.out.println(endDateTime);

        Period period = new Period(startDateTime,endDateTime);

        Hours hours = Hours.hoursBetween(startDateTime, endDateTime);

        Weeks weeks = Weeks.weeksBetween(startDateTime, endDateTime);

        //starting first week add 1 follow ghost cat
        System.out.println(hours.getHours()/(24*7*2) + 1);

        System.out.println(weeks.getWeeks()/(2) + 1);

    }

}

在我看来,您只需要在所有情况下将计算结果加 1,除非结束日期 正好 在同一时间之后的同一时间间隔(除非您将其计算为一个事件)。

╔══════════════════╤══════════════════╤══════════════╤══════════════╤════════════╗
║ Start Date       │ End Date         │ result of    │ result of    │ Expected   ║
║                  │                  │ weeksBetween │ dividedBy(2) │ result     ║
╠══════════════════╪══════════════════╪══════════════╪══════════════╪════════════╣
║ 2016-06-07 09:00 │ 2016-08-30 10:00 │ 12           │ 6            │ 7          ║
╟──────────────────┼──────────────────┼──────────────┼──────────────┼────────────╢
║ 2016-06-07 09:00 │ 2016-08-30 08:00 │ 11           │ 5            │ 6          ║
╟──────────────────┼──────────────────┼──────────────┼──────────────┼────────────╢
║ 2016-06-07 09:00 │ 2016-08-30 09:00 │ 12           │ 6            │ 6 or 7     ║
║                  │                  │              │              │ you decide ║
╟──────────────────┼──────────────────┼──────────────┼──────────────┼────────────╢
║ 2016-06-07 09:00 │ 2016-08-23 10:00 │ 11           │ 5            │ 6          ║
╟──────────────────┼──────────────────┼──────────────┼──────────────┼────────────╢
║ 2016-06-07 09:00 │ 2016-08-23 08:00 │ 10           │ 5            │ 6          ║
╟──────────────────┼──────────────────┼──────────────┼──────────────┼────────────╢
║ 2016-06-07 09:00 │ 2016-08-23 09:00 │ 11           │ 5            │ 6          ║
╚══════════════════╧══════════════════╧══════════════╧══════════════╧════════════╝

采用第一个组合 - 结束日期略晚于假设的最后一次出现:

Start
──╂──────┼──────┼──────┼──────┼──────┼──────┼─┰╴
                                             end
      1      2      3      4      5     6

Joda 计算的是 差距。您要计算的是滴答声。而且跳动总是比跳空多一个。

在第二种情况下,结尾比下一次出现早一点:

Start
──╂──────┼──────┼──────┼──────┼──────┼─────┰┼─╴
                                          end
      1      2      3      4      5

这次Joda说开始和结束之间有5个整"periods",但是你要算的是那些ends,所以永远是一个。

您可能想要决定不添加一个的唯一情况是,如果结尾恰好下一次,在这种情况下,您可能希望在考虑时将其排除"already ended".

如果是这样,您只需为其添加一个测试:

Weeks weeks = Weeks.weeksBetween(startDateTime, endDateTime);
int occurrences = weeks.dividedBy(2).getWeeks();
if ( ! startDateTime.plus(weeks).equals(endDateTime) ) {
    occurrences++;
}