W3docs

Java Calendar Class

The legacy java.util.Calendar class and GregorianCalendar — how they relate to java.time.

Java Calendar Class

java.util.Calendar is the second half of the legacy date/time pair. Where java.util.Date is just a wrapper around a long of epoch milliseconds, Calendar is the thing that knows what year, month, and day that millisecond falls on. It was added in Java 1.1 to take the calendar arithmetic out of Date — that's why most of Date's field accessors are deprecated.

In modern code you should reach for java.time. But Calendar still shows up: in old code paths, in libraries written before Java 8, and in JDBC drivers that haven't been updated. You need to read it, bridge it, and move on.

Calendar is abstract

Calendar itself is an abstract class. You never call new Calendar(...). You get an instance through the factory:

Calendar cal = Calendar.getInstance();

This returns a concrete subclass — almost always GregorianCalendar — pre-loaded with the current time, the default time zone, and the default locale. You can be explicit if you need to:

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.US);

GregorianCalendar has public constructors too, but the factory is the convention.

Field constants

Calendar exposes everything through integer field constants and a generic get(int field):

FieldMeaningRange
Calendar.YEARYearfull year, e.g. 2026
Calendar.MONTHMonth0–11 (January = 0)
Calendar.DAY_OF_MONTHDay of month1–31
Calendar.DAY_OF_WEEKDay of week1–7 (Sunday = 1)
Calendar.HOUR_OF_DAYHour0–23
Calendar.MINUTEMinute0–59
Calendar.SECONDSecond0–59
Calendar.MILLISECONDMilli0–999

Two traps live in this table: MONTH is zero-based (Calendar.JANUARY == 0) and DAY_OF_WEEK starts at Sunday. Off-by-one bugs in legacy code almost always trace back here.

Setting and arithmetic

set takes a field and a value; add does calendar-aware arithmetic that rolls overflow into bigger fields:

Calendar cal = Calendar.getInstance();
cal.set(2026, Calendar.MAY, 29);   // Y, M, D — month is zero-based
cal.add(Calendar.DAY_OF_MONTH, 5); // → 2026-06-03

roll does the same field-by-field but does not carry over into bigger fields — roll(DAY_OF_MONTH, 5) on May 30 gives May 4, not June 4. It's rarely what you want.

Calendar instances are mutable, so passing one to a method means handing out a live handle. Clone before exposing, the same way you would with a Date.

The bridge to java.time

The whole point of touching Calendar today is to get out of it. Two methods do that:

Instant when = cal.toInstant();
ZoneId  zone = cal.getTimeZone().toZoneId();
ZonedDateTime zdt = when.atZone(zone);

From ZonedDateTime you have the whole modern API. Going the other way:

Calendar cal = GregorianCalendar.from(zdt); // Java 8+

GregorianCalendar.from(ZonedDateTime) is the supported conversion. Avoid round-tripping through Date.

Worked example

The example below shows the two things you'll do with Calendar in real code: read fields off a legacy instance, and bridge to java.time to do anything non-trivial.

java— editable, runs on the server

What to take from the run:

  • Calendar.MONTH for May prints 4. Zero-based months are the single biggest source of bugs in legacy date code.
  • getInstance(TimeZone) ties the instance to a zone — different from Date, which has none.
  • add(MONTH, 1) understands month length; you get the right day in the next month even though months aren't all 30 days.
  • cal.toInstant().atZone(cal.getTimeZone().toZoneId()) is the one-line bridge into java.time.
  • GregorianCalendar.from(ZonedDateTime) lets you hand a Calendar back to an old API without losing the zone.

What's next

That closes Part 14 — Date and Time. Next up is Part 15, Multithreading and Concurrency, beginning with Multithreading in Java — the model that has shaped every other API in this book.

Practice

A Calendar set to May 29, 2026 reports cal.get(Calendar.MONTH). What value comes back?