使用Joda Time计算月份差异

9

在代码的第四行(忽略空格和注释)以及之后,我正在计算两个日期之间的月份差异。这样做是可以的,但看起来有点不专业。是否有更好的方法?

int handleAllowance(LocalDate today) {

    int allowance = membership.allowance();
    if (allowance == 0) return 0;

    // if update was last month (or earlier)
    int months = today.monthOfYear().getMaximumValue() - today.monthOfYear().getMinimumValue(); // yeah, 12, but just to be 100% correct :-)
    int curMonth = (today.getYear()               * months) + today.              getMonthOfYear();
    int updMonth = (lastAllowanceUpdate.getYear() * months) + lastAllowanceUpdate.getMonthOfYear();
    if (curMonth > updMonth) {

        // ...and if today is on or past update day
        int updateDay = Math.min(allowanceDay, today.dayOfMonth().getMaximumValue());
        if (today.getDayOfMonth() >= updateDay) {

            // number of months to give allowance (in the rare case this process fails to run for 2 months or more)
            int allowanceMonths = curMonth - updMonth;

            // give credits
            final int totalAllowance = allowance * allowanceMonths;
            giveCredits(totalAllowance);

            // update day
            lastAllowanceUpdate = lastAllowanceUpdate.plusMonths(allowanceMonths);

            // return the allowance given
            return totalAllowance;

        }

    }

    return 0;
}

一个问题,虽然不是主题:allowance 是常量吗?如果不是,那么 final int totalAllowance = allowance * allowanceMonths; 对于 allowanceMonths > 1 可能会产生与运行整个部分 allowanceMonths 次数不同的结果。 - Thomas
定义常量。它的值取决于用户的订阅类型,这个类型可能会在2个月的期间内发生变化。但是它不会以任何其他方式改变。所以我想,如果该进程在2个月内未能运行,并且用户决定在再次运行之前升级订阅,则他将获得比预期更多的津贴。虽然这没关系,它不是致命错误,而且allowanceMonths很可能永远不会真正大于1。 - Bart van Heukelom
我所说的“常量”是指“在两次运行之间值是否会改变,结果是否取决于信用额度或类似因素”?由于您似乎有一个恒定的津贴(除非用户更改订阅类型),这可能是可以接受的。您还可以为每个月执行此操作,并获取每个月的订阅类型以获得更准确的结果。 - Thomas
@Thomas:我可以做,但目前无法找到用户过去的订阅,而且也不值得花费精力使其成为可能。 - Bart van Heukelom
3个回答

27
Months.monthsBetween(
     start.withDayOfMonth(1),
     end.withDayOfMonth(1)).getMonths()

1
如果date1是1月31日,date2是2月1日,那么这样返回0不就错了吗?我基本上需要舍弃日期信息,然后计算差异...哦,等等,我可以这样做。:p - Bart van Heukelom
我把我的解决方案发布为答案。 - Bart van Heukelom
2
我使用了这个代码,但是出现了以下错误信息:org.joda.time.IllegalFieldValueException: Value 0 for dayOfMonth must be in the range [1,28] - zeddarn
@zeddarn 是的。在Yoda时间中,月份从1开始(天数也是),与Calendar 0不同。如果您从Calendar转换到Yoda,请记得添加+1。 - Matt Leonowicz

2
这与Bozho的解决方案非常相似:
  public static YearMonth toYearMonth(LocalDate localDate) {
    return new YearMonth(localDate.getYear(), localDate.getMonthOfYear());
  }

  public static int monthSwitches(LocalDate date1,LocalDate date2) {
    return Months.monthsBetween(toYearMonth(date1),toYearMonth(date2)).getMonths();
  }

这个解决方案适用于“29/01/2016”和“01/02/2016”这样的情况! - Lucas Oliveira

1

这是我在评论 Bozho 时想出的解决方案

int handleAllowance(LocalDate today) {

    int allowance = membership.allowance();
    if (allowance == 0) return 0;

    // calculate month difference
    int allowanceMonths = Months.monthsBetween(lastAllowanceUpdate.withDayOfMonth(1), today.withDayOfMonth(1)).getMonths();

    // if update was last month or earlier
    if (allowanceMonths > 0) {

        // ...and if today is on or past update day
        int updateDay = Math.min(allowanceDay, today.dayOfMonth().getMaximumValue());
        if (today.getDayOfMonth() >= updateDay) {

            // give credits (multiply with months in the rare case this process consecutively fails to run for 2 months or more)
            final int totalAllowance = allowance * allowanceMonths;
            giveCredits(totalAllowance);

            // update day
            lastAllowanceUpdate = lastAllowanceUpdate.plusMonths(allowanceMonths);

            // return the allowance given
            return totalAllowance;

        }

    }

    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接