Skip to content
Advertisement

Number of days in leap years with IsoChronology

2000 CE is a leap year with 366 days. 2001 CE and 2002 CE do not leap years and have 355 days.

Logically, if I ask the number of days between January 1st, 2000 CE and January 1st, 2001 CE, I should get one more day than between January 1st, 2001 CE and January 1st, 2002 CE.

This is not what I observe, see code below.

        IsoChronology isoc = IsoChronology.INSTANCE;

        LocalDate date1;
        LocalDate date2;
        Period period;

        // Between January 1st, 0 CE and January 1st, 1 CE

        date1 = isoc.dateYearDay(2000, 1);
        date2 = isoc.dateYearDay(2001, 1);
        period = Period.between(date1, date2);

        assertTrue(isoc.isLeapYear(2000L));
        assertFalse(isoc.isLeapYear(2001L));
        assertEquals("2000-01-01", date1.toString());
        assertEquals("2001-01-01", date2.toString());
        assertEquals("P1Y", period.toString());

        // Between January 1st, 1 CE and January 1st, 2 CE

        date1 = isoc.dateYearDay(2001, 1);
        date2 = isoc.dateYearDay(2002, 1);
        period = Period.between(date1, date2);

        assertFalse(isoc.isLeapYear(2001L));
        assertFalse(isoc.isLeapYear(2002L));
        assertEquals("2001-01-01", date1.toString());
        assertEquals("2002-01-01", date2.toString());
        assertEquals("P1Y", period.toString());

EDIT

Modified the example to use 400 CE, 401 CE and 402 CE since 0 CE is not a “real” year (1 BCE and 1 CE are real, but 0 BCE = 1 CE and 1 BCE = 0 CE).

EDIT

Modified the examples to use 2000 CE, 2001 CE and 2002 CE to use years in the (non-proleptic) Gregorian calendar.

Additionally removed irrelevant information.

EDIT

Period.toString() includes the days when they are not 0. Period.of(1 /* year /, 0 / month /, 1 / days */) renders as P1Y1D, so I would have expected one of the example to be different from the other one.

COMPLEMENT

According to the ISO chronology and Period, one month and one day after January 28th 2001 is March 1st 2001. But one month and one day before that date is January 31st 2001.

So in this case we end up 3 days later than where we started from.

On a leap year, we would end up on the same date in this case.

        IsoChronology chronology = IsoChronology.INSTANCE;

        Period oneMonthOneDay = Period.of(0, 1, 1);

        Temporal january28th2001 = chronology.date(2001, 1, 28);
        Temporal march1st2001 = oneMonthOneDay.addTo(january28th2001);
        Temporal january31th2001 = oneMonthOneDay.subtractFrom(march1st2001);
        Assert.assertEquals("2001-01-28", january28th2001.toString());
        Assert.assertEquals("2001-03-01", march1st2001.toString());
        Assert.assertEquals("2001-01-31", january31th2001.toString());

        Temporal january28th2004 = chronology.date(2004, 1, 28);
        Temporal february29th2004 = oneMonthOneDay.addTo(january28th2004);
        Temporal january28th2004Again = oneMonthOneDay.subtractFrom(february29th2004);
        Assert.assertEquals("2004-01-28", january28th2004.toString());
        Assert.assertEquals("2004-02-29", february29th2004.toString());
        Assert.assertEquals("2004-01-28", january28th2004Again.toString());

Answer

It is technically not wrong. Between 2000-01-01 and 2001-01-01, it is the period of “1 year” (“P1Y”). Between 2001-01-01 and 2002-01-01, it is also the period of “1 year” (“P1Y”). Nowhere does it say 365 or 366 days. You can’t actually get the number 365 or 366 from the period you’ve got.

You are not asking how many days there are, between the two dates here:

Period.between(date1, date2)

You are asking for the period.

A Period is a number of years, months and days, and you just so happen to get the period of “1 year 0 months 0 days” as the output.

Note that period calculations are very different from those you are used to in maths. Adding “1 month” to the Feb 1 adds 28 or 29 days, but adding “1 month” to March 1 adds 30 days. This also means that adding “1 month” 10 times to a date could give you a different result than adding “10 months” all in one go.

You might be wondering why doesn’t Period.between give you the number of days between the dates, but rather just says “1 year”. First, note that “1 year” and “365 days” are very different periods. Adding “1 year” to 2020-01-01 would give you 2021-01-01, but adding “365 days” to 2020-01-01 would give you 2020-12-31.

If Period.between returned “365 days” or “366 days” instead, you wouldn’t be able to, e.g. easily ask the user to input two dates, and continue to output dates at that interval. Consider:

public void iterateDatesAtRegularInterval(LocalDate date1, LocalDate date2) {
    Period p = Period.between(date1, date2);
    LocalDate current = date2;
    while (some end condition) {
        current = current.plus(p);
        System.out.println(current);
    }
}

If Period.between returned “365 days” or “366 days” instead and I happen to pass 2020-01-01 and 2021-01-01 (366 days), the next thing this method would output would be 2022-01-02, which IMO would be quite unexpected for most people.

Period.between basically picks the largest units that it can, because this is most likely going to be the most useful to you, if you are doing period calculations.

If you want the result of 365/366 days though, don’t use Period. Use something like:

assertEquals(365L, ChronoUnit.DAYS.between(date1, date2))
Advertisement