javadatedatetimecalendarjava-calendar

What's the difference between Calendar's getActualMinimum and getGreatestMinimum methods?


Since getActualMinimum() and getGreatestMinimum() are going to return the same value for every field what's the exact difference?


Solution

  • The getActualMinimum() method returns the minimum possible value that an attribute could possibly have. For example, Calendar.DAY_OF_MONTH is never less than 1, since no month starts with a day numbered 0 or less.

    The getGreatestMinimum() method returns the maximum possible value that getActualMinimum() could ever have. While in most cases these values will always be the same (months nearly always start with 1), the code allows for them to be different in some rare circumstances. Using getGreatestMinimum() is appropriate for data validation scenarios.

    One place this can (and does) occur is when there are skipped dates or discontinuities in a calendar. Java's GregorianCalendar, for example implements dates in the Julian calendar before a cutover date, and the Gregorian calendar after that date, and thus has a gap of several days where dates in the calendar simply don't exist.

    The Calendar class getGreatestMinimum() method is abstract, so it is required to be implemented by a subclass. The JRE's implementation for GregorianCalendar shows that it could differ on the DAY_OF_MONTH field if the "skipped days" on the cutover month don't include the first of the month:

    public int getGreatestMinimum(int field) {
        if (field == DAY_OF_MONTH) {
            BaseCalendar.Date d = getGregorianCutoverDate();
            long mon1 = getFixedDateMonth1(d, gregorianCutoverDate);
            d = getCalendarDate(mon1);
            return Math.max(MIN_VALUES[field], d.getDayOfMonth());
        }
        return MIN_VALUES[field];
    }
    

    Using this as a cue, you can use setGregorianChange() to set the cutover date for Julian-to-Gregorian change. By default it is 15 October 1582 (having skipped 10 days from 4 October 1582). Different Locales switched at different times, for example Great Britain switched on 14 September 1752 (the day after 2 September).

    By setting this cutover to a date in a month that makes the skipped days overlap the first of the month, we can generate these edge cases.

    An actual locale-based discontinuity with a real cutover date is probably Romania, in which the day after 31 March 1919 was 14 April 1919. So in a Romanian locale, the month of April 1919 would only include days from 14 to 30, and getGreatestMin() will return 14.

    Since I don't have that locale installed I can change the cutover date to simulate what would happen:

    GregorianCalendar cal = new GregorianCalendar();
    // Deprecated, but an easy way of showing this example
    cal.setGregorianChange(new Date(1911, Calendar.APRIL, 14));
    System.out.println("actual min = " + cal.getActualMinimum(cal.DAY_OF_MONTH));
    System.out.println("greatest min = " + cal.getGreatestMinimum(cal.DAY_OF_MONTH));
    

    Output:

    actual min = 1
    greatest min = 14
    

    Another edge case exists if you use Date(Long.MIN_VALUE) as the cutover, giving a "pure Gregorian" calendar with no gaps. But in that case, the "beginning of time" is on the 16th of the month (in the Gregorian calendar), so greatest min in that case is 16.

    Other calendar systems may have similar discontinuities and edge cases.