00:00

Calendar Class in Java – Fields, Date Arithmetic and java.time

java.util.Calendar is an abstract class, introduced in Java 1.1, for working with the fields of a date (year, month, day, hour and so on) and for date arithmetic, things that java.util.Date cannot do well. Its standard implementation is GregorianCalendar.

Like Date, Calendar is now considered legacy: the java.time API introduced in Java 8 is simpler, immutable and thread-safe. This tutorial shows how Calendar works, its pitfalls, and how to convert to java.time.

1. Getting and Setting Fields

Obtain an instance with Calendar.getInstance() (default zone and locale, or the ones you pass). Fields are read and written with constants such as Calendar.YEAR:

CalendarFields.java
import java.util.*;

public class CalendarFields {
    public static void main(String[] args) {
        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Asia/Kolkata"), Locale.ENGLISH);
        cal.clear();
        cal.set(2026, Calendar.OCTOBER, 11, 15, 30, 0);       // month constant avoids the 0-based trap

        System.out.println("YEAR         : " + cal.get(Calendar.YEAR));
        System.out.println("MONTH        : " + cal.get(Calendar.MONTH) + " (0-based: 9 = October)");
        System.out.println("DAY_OF_MONTH : " + cal.get(Calendar.DAY_OF_MONTH));
        System.out.println("DAY_OF_WEEK  : " + cal.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.ENGLISH));
        System.out.println("DAY_OF_YEAR  : " + cal.get(Calendar.DAY_OF_YEAR));
        System.out.println("HOUR_OF_DAY  : " + cal.get(Calendar.HOUR_OF_DAY));
        System.out.println("WEEK_OF_YEAR : " + cal.get(Calendar.WEEK_OF_YEAR));
        System.out.println("max days in month: " + cal.getActualMaximum(Calendar.DAY_OF_MONTH));
        System.out.println("class        : " + cal.getClass().getSimpleName());
    }
}
Output
YEAR         : 2026
MONTH        : 9 (0-based: 9 = October)
DAY_OF_MONTH : 11
DAY_OF_WEEK  : Sunday
DAY_OF_YEAR  : 284
HOUR_OF_DAY  : 15
WEEK_OF_YEAR : 42
max days in month: 31
class        : GregorianCalendar
Field constantMeaning
YEAR, MONTH, DAY_OF_MONTHDate parts (MONTH is 0-based)
HOUR_OF_DAY, HOUR, MINUTE, SECOND, MILLISECONDTime parts (HOUR is 12-hour)
DAY_OF_WEEKSUNDAY (1) to SATURDAY (7)
DAY_OF_YEAR, WEEK_OF_YEARPosition in the year (week rules depend on locale)
AM_PM, ERA, ZONE_OFFSETOther fields

Use getTime() to get a Date, getTimeInMillis() for epoch milliseconds, and clear() before set so leftover time fields do not leak into your value.

2. Date Arithmetic: add() vs roll()

CalendarArithmetic.java
import java.text.SimpleDateFormat;
import java.util.*;

public class CalendarArithmetic {
    public static void main(String[] args) {
        TimeZone ist = TimeZone.getTimeZone("Asia/Kolkata");
        SimpleDateFormat fmt = new SimpleDateFormat("EEE dd MMM yyyy", Locale.ENGLISH);
        fmt.setTimeZone(ist);

        Calendar cal = Calendar.getInstance(ist, Locale.ENGLISH);
        cal.clear();
        cal.set(2026, Calendar.JANUARY, 31);
        System.out.println("start          : " + fmt.format(cal.getTime()));

        cal.add(Calendar.MONTH, 1);              // add: adjusts to the last valid day
        System.out.println("add 1 month    : " + fmt.format(cal.getTime()));

        cal.add(Calendar.DAY_OF_MONTH, 30);
        System.out.println("add 30 days    : " + fmt.format(cal.getTime()));

        cal.roll(Calendar.MONTH, 11);            // roll: changes one field without carrying over
        System.out.println("roll month +11 : " + fmt.format(cal.getTime()));

        Calendar other = (Calendar) cal.clone();
        other.add(Calendar.YEAR, -1);
        System.out.println("other before cal: " + other.before(cal));
    }
}
Output
start          : Sat 31 Jan 2026
add 1 month    : Sat 28 Feb 2026
add 30 days    : Mon 30 Mar 2026
roll month +11 : Sat 28 Feb 2026
other before cal: true
  • add(field, amount) changes a field and carries over into larger fields; adding a month to 31 January gives 28 February.
  • roll(field, amount) changes only that field and wraps within its range, leaving larger fields (here, the year) unchanged.
  • Calendar is mutable: add modifies the object. clone() it when you need to keep the original.
  • Compare with before, after and compareTo.

3. Common Pitfalls

  • Zero-based months: set(2026, 10, 11) means 11 November. Use the constants Calendar.OCTOBER etc.
  • Mutability and thread safety: sharing a Calendar between threads or objects leads to surprising changes.
  • Lenient by default: invalid values such as 32 January silently roll over to 1 February unless you call setLenient(false).
  • Locale-dependent: the first day of the week and week numbering vary by locale.

4. Converting to java.time

GregorianCalendar converts directly to ZonedDateTime and back. The same arithmetic in java.time is shorter and works on immutable values:

CalendarToJavaTime.java
import java.time.*;
import java.util.*;

public class CalendarToJavaTime {
    public static void main(String[] args) {
        GregorianCalendar cal = new GregorianCalendar(2026, Calendar.OCTOBER, 11);
        cal.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));

        ZonedDateTime zdt = cal.toZonedDateTime();                 // legacy -> modern
        System.out.println("toZonedDateTime : " + zdt.toLocalDate());

        LocalDate modern = LocalDate.of(2026, 1, 31).plusMonths(1).plusDays(30);   // same arithmetic, immutable
        System.out.println("java.time result: " + modern);

        GregorianCalendar back = GregorianCalendar.from(zdt);     // modern -> legacy
        System.out.println("round trip year : " + back.get(Calendar.YEAR));
    }
}
Output
toZonedDateTime : 2026-10-11
java.time result: 2026-03-30
round trip year : 2026
Calendar codejava.time equivalent
Calendar.getInstance()ZonedDateTime.now() / LocalDateTime.now()
cal.get(Calendar.YEAR)date.getYear()
cal.get(Calendar.MONTH) + 1date.getMonthValue()
cal.add(Calendar.DAY_OF_MONTH, 5)date.plusDays(5) (returns a new object)
cal.getActualMaximum(DAY_OF_MONTH)date.lengthOfMonth()

5. Summary

  • Calendar (usually a GregorianCalendar) reads, sets and calculates date fields.
  • add carries over to larger fields; roll does not.
  • Watch out for zero-based months, mutability and lenient parsing.
  • Prefer java.time in new code and convert with toZonedDateTime() and GregorianCalendar.from().

Related Tutorials