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):
| Field | Meaning | Range |
|---|---|---|
Calendar.YEAR | Year | full year, e.g. 2026 |
Calendar.MONTH | Month | 0–11 (January = 0) |
Calendar.DAY_OF_MONTH | Day of month | 1–31 |
Calendar.DAY_OF_WEEK | Day of week | 1–7 (Sunday = 1) |
Calendar.HOUR_OF_DAY | Hour | 0–23 |
Calendar.MINUTE | Minute | 0–59 |
Calendar.SECOND | Second | 0–59 |
Calendar.MILLISECOND | Milli | 0–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-03roll 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.
What to take from the run:
Calendar.MONTHfor May prints4. Zero-based months are the single biggest source of bugs in legacy date code.getInstance(TimeZone)ties the instance to a zone — different fromDate, 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 intojava.time.GregorianCalendar.from(ZonedDateTime)lets you hand aCalendarback 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.
A Calendar set to May 29, 2026 reports cal.get(Calendar.MONTH). What value comes back?