计算两个日期之间的天数,忽略年份

时间:2019-04-18 02:10:02

标签: java java-8

我有“ LocalDate”类型的结婚日期列表,我想找到在接下来的30天内有周年纪念日的人的列表。所有的结婚日期都来自过去的年份,例如1980、1990、2000 ...

我尝试使用ChronoUnit.DAYS.between()函数,但是如果日期是今天和将来的日期,它只会显示天数。

String str = "2019-04-24";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse(str, formatter);
LocalDate today = LocalDate.now();          
long dd = ChronoUnit.DAYS.between(today, date);

我希望得到的答案是,如果结婚日期是1990-04-29,那么它应该告诉我该结婚日期是否在接下来的30天内。意思是,上述日期的周年纪念日是否在接下来的30天内。

4 个答案:

答案 0 :(得分:3)

此计算所需的测试用例为:

Today        Marriage     Days between
2000-01-01   ????-01-01     0
2000-01-01   ????-01-02     1
2000-01-01   ????-01-31    30
2000-01-01   ????-12-31   365   since 2000 is a leap year
2001-01-01   ????-12-31   364   since 2001 is not a leap year
2000-02-28   ????-03-01     2   since 2000 is a leap year
2000-12-31   ????-01-01     1
2000-12-01   ????-11-30   364   since 2001 is not a leap year
1999-12-01   ????-11-30   365   since 2000 is not a leap year

这些测试用例提示了如何做。

  1. 结婚日期
  2. 尝试使用当年的日期
  3. 如果是过去的日期,则以第二年的日期为准。
  4. 计算今天和该日期之间的天数

第4步将处理of年。

我建议编写这种方法:

int daysUntilNextAnniversary(LocalDate today, LocalDate anniversary) {
    ...
}

anniversary 一词已经包含了与周年纪念日无关的信息,与上面的测试案例相符。

然后您可以像这样轻松地使用它:

int days = daysUntilNextAnniversary(LocalDate.now(), marriage);
if (1 <= days && days <= 30) {
    ...
}

以下是上述测试用例的测试代码:

package de.roland_illig.so;

import static org.junit.Assert.assertEquals;

import java.time.LocalDate;
import org.junit.Test;

public class AnnivTest {

    // Interesting test cases are:
    //
    // Marriage:
    // - Jan 01 in leap year
    // - Feb 29 in leap year
    // - Dec 31 in leap year
    // - Jan 01 in year after leap year
    //
    // Today:
    // - Jan 01 in leap year
    // - Feb 28 in leap year
    // - Feb 28 in year before leap year
    // - Feb 29 in leap year
    // - Dec 31 in leap year
    // - Dec 31 in year before leap year
    //
    // Ideally the test would test every combination of marriage and today.
    @Test
    public void daysUntilNextAnniversary() {
        test("2000-01-01", "01-01", 0);
        test("2000-01-01", "01-02", 1);
        test("2000-01-01", "01-31", 30);
        test("2000-01-01", "12-31", 365); // since 2000 is a leap year
        test("2001-01-01", "12-31", 364); // since 2001 is not a leap year
        test("2000-02-28", "03-01", 2); //   since 2000 is a leap year
        test("2000-12-31", "01-01", 1);
        test("2000-12-01", "11-30", 364); // since 2001 is not a leap year
        test("1999-12-01", "11-30", 365); // since 2000 is not a leap year

        // Ensures that the marriage is not moved to Feb 28 just
        // because the current year doesn't have Feb 29. This happens
        // when an intermediate result is 2019-02-29, which is corrected
        // to 2019-02-28.
        test("2019-12-31", "02-29", 60);

        // In a non-leap-year, Feb 28 and Feb 29 are merged into one day.
        test("2019-02-28", "02-29", 0);
    }

    private void test(String today, String marriage, int expectedDays) {
        int actual = Anniv.daysUntilNextAnniversary(
                LocalDate.parse(today),
                LocalDate.parse("1996-" + marriage));
        assertEquals(expectedDays, actual);
    }
}

这是计算的实际代码:

package de.roland_illig.so;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public class Anniv {

    public static int daysUntilNextAnniversary(LocalDate today, LocalDate anniversary) {
        LocalDate d = anniversary.withYear(today.getYear());
        if (d.isBefore(today)) {
            d = anniversary.withYear(today.getYear() + 1);
        }
        return Math.toIntExact(ChronoUnit.DAYS.between(today, d));
    }
}

如您所见,测试代码比实际的应用程序代码长得多。在处理日期计算时,这是必需的。当不同时区发挥作用时,甚至更多。 leap秒。和其他日历异常。

答案 1 :(得分:1)

因此,“基本”概念是,您希望获取日期列表并更改年份以匹配今年。 “捕获”是指,如果结果日期在今天之前,则应将年份加1,这样,如果现在是12月,则将捕获在1月发生的所有周年纪念日。

也许像...

one = ['Here is a striiing']
two = ['Here is another string']
asdf = list(d.compare(one, two))
for item in asdf:
    print(item)

> - Here is a striiing
> ?               --
>
> + Here is another string
> ?          ++++++

然后,您只需检查日期是否在所需范围内...

LocalDate now = LocalDate.now();
int year = now.getYear();
List<LocalDate> dates = ...;
List<LocalDate> adjusted = new ArrayList<>(10);
for (LocalDate date : dates) {
    LocalDate warped = date.withYear(year);
    if (warped.isBefore(now)) {
        warped = warped.withYear(year + 1);
    }
    adjusted.add(warped);
}

因此,使用相同的伪随机生成的日期,我可以获得类似...的结果

LocalDate limit = now.plusDays(30);
for (LocalDate date : adjusted) {
    if ((date.isAfter(now) || date.isEqual(now)) && (date.isBefore(limit) || date.isEqual(limit))) {
        System.out.println("~~ " + date);
    }
}

如果我们将锚定日期更改为Input date 2019-04-19 +---------------+---------------+--------------+ | Original Date | Adjusted Date | Within range | +---------------+---------------+--------------+ | 1996-04-13 | 2020-04-13 | | | 1986-04-24 | 2019-04-24 | X | | 1989-04-23 | 2019-04-23 | X | | 1960-05-11 | 2019-05-11 | X | | 1986-05-18 | 2019-05-18 | X | | 1984-04-06 | 2020-04-06 | | | 1997-05-29 | 2019-05-29 | | | 2008-03-31 | 2020-03-31 | | | 2014-04-18 | 2020-04-18 | | | 1982-04-23 | 2019-04-23 | X | +---------------+---------------+--------------+ 之类的内容,则可能会生成类似...的内容。

2019-12-20

因此它正在捕获明年的日期。

nb:我会随机生成测试日期,该日期应在锚定日期的+/-一个月内,以便获得更好的测试数据。

答案 2 :(得分:0)

由于您根本不关心年份:

private static boolean isWithinNext30Days(LocalDate date, LocalDate today) {

    int todayYear = today.getYear();
    int todayMonth = today.getMonthValue();

    int dateMonth = date.getMonthValue();

    if (todayMonth > dateMonth) {
        date = date.withYear(todayYear + 1);
        return today.plusDays(30).isAfter(date) && date.isAfter(today);
    }

    LocalDate alteredDate = date.withYear(todayYear);
    return today.plusDays(30).isAfter(alteredDate) && alteredDate.isAfter(today);

}

答案 3 :(得分:-1)

尝试计算今年的周年纪念日,找出现在和那个日期之间的差额:

    String str = "2008-05-20";
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    LocalDate date = LocalDate.parse(str, formatter);
    LocalDate today = LocalDate.now();          
    long dd = ChronoUnit.DAYS.between(today, date);
    System.err.println("YOUR ANSWER WAS " + dd);

    LocalDate thisYearsAnniversary = LocalDate.of(today.getYear(), date.getMonth(), date.getDayOfMonth());
    System.err.println("THIS YEARS ANNIVERSARY IS " + thisYearsAnniversary);

    dd = ChronoUnit.DAYS.between(today, thisYearsAnniversary);
    System.err.println("WHICH IS IN " + dd + " days");

输出

YOUR ANSWER WAS -3981
THIS YEARS ANNIVERSARY IS 2019-05-24
WHICH IS IN 36 days