Java Calendar Class and Date: get(), add(), roll() and Conversion
Run Calendar.getInstance().get(Calendar.MONTH) on 18 October 2026 and Java prints 9, not 10. The Calendar class counts months from 0 (January) to 11 (December), and the older Date class goes further: new Date(2024, 1, 15) creates 15 February of the year 3924. Both quirks still break real code, from server-side reports to Android date pickers.

The Java Calendar class (java.util.Calendar) is an abstract class that converts a moment in time into fields — year, month, day, hour, minute, week number — and lets you read them with get() and change them with set(), add() and roll(). The Date class (java.util.Date) stores that moment as milliseconds since 1970-01-01T00:00:00 UTC. Together they form the legacy date-time API of Java. New code uses the Date-Time API (java.time), but Calendar and Date are still everywhere in older projects, JDBC code, Android apps and third-party libraries.
1. Java Calendar class and GregorianCalendar
Calendar is an abstract class, so you never call new Calendar(). You get an instance with Calendar.getInstance() (current moment, default time zone and locale) or create a GregorianCalendar directly, then read individual fields with calendar.get(Calendar.FIELD).
GregorianCalendar is the only public subclass of Calendar in java.util, but not the only one in the JDK. For the th_TH locale, Calendar.getInstance() returns sun.util.BuddhistCalendar (Buddhist calendar, the year is 543 higher), and for ja_JP_JP it returns java.util.JapaneseImperialCalendar (Japanese imperial eras, a package-private class). Code that casts (GregorianCalendar) Calendar.getInstance() fails with ClassCastException on those locales.
Java Calendar get(): the most used fields
| Field | What get() returns | Range / note |
|---|---|---|
Calendar.YEAR | Year | The real year, for example 2026 (no 1900 offset) |
Calendar.MONTH | Month | 0 (JANUARY) to 11 (DECEMBER) — add 1 for display |
Calendar.DAY_OF_MONTH | Day of the month | 1 to 31 — days start from 1 |
Calendar.DAY_OF_WEEK | Day of the week | 1 (SUNDAY) to 7 (SATURDAY) in every locale |
Calendar.HOUR_OF_DAY / Calendar.HOUR | Hour | 0 to 23 / 0 to 11 together with AM_PM |
Calendar.WEEK_OF_YEAR, WEEK_OF_MONTH | Week number | Depends on the locale (first day of week, minimal days in first week) |
The same constants work with set(): calendar.set(Calendar.MONTH, Calendar.MARCH) changes one field, and calendar.set(2026, Calendar.OCTOBER, 18) sets year, month and day at once.
Example: reading date fields with get()
import java.util.Calendar;
public class CalendarExample2 {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println("Month " + calendar.get(Calendar.MONTH));
System.out.println("Year " + calendar.get(Calendar.YEAR));
System.out.println("Week of month "
+ calendar.get(Calendar.WEEK_OF_MONTH));
System.out.println("Week of year "
+ calendar.get(Calendar.WEEK_OF_YEAR));
System.out.println("Day of year " + calendar.get(Calendar.DAY_OF_YEAR));
System.out.println("Day of month "
+ calendar.get(Calendar.DAY_OF_MONTH));
if (Calendar.MONDAY == calendar.getFirstDayOfWeek()) {
System.out.println("First day of week is Monday");
}
}
} Output when run on 18 October 2026 with the en_US locale (the result depends on the current date and locale):
Month 9
Year 2026
Week of month 4
Week of year 43
Day of year 291
Day of month 18 "Month 9" is October, not September: Calendar.MONTH returns 0 for January and 11 for December. Add 1 to get the usual month number, and compare with constants such as Calendar.OCTOBER instead of plain numbers.
Same code, different week numbers. Week fields depend on the locale. In en_US the week starts on Sunday and week 1 is the week that contains 1 January, so the Monday line is not printed. Run the same program on the same day with ru_RU (which follows ISO 8601: weeks start on Monday, week 1 must contain at least 4 days of the new year) and three lines change:
Week of month 3
Week of year 42
First day of week is Monday If week numbers go into reports or file names, pass the locale explicitly — Calendar.getInstance(Locale.US) — instead of relying on the default locale of the server.
2. How to convert Calendar to Date and back
To convert Calendar to Date in Java, call calendar.getTime(); to go from Date to Calendar, call calendar.setTime(date). Both classes hold the same moment in milliseconds, so the conversion is lossless, and calendar.getTimeInMillis() returns the same number as date.getTime().
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
public class CalendarToDate {
public static void main(String[] args) {
Calendar calendar = new GregorianCalendar(2026, Calendar.OCTOBER, 18, 14, 30);
Date date = calendar.getTime(); // Calendar -> Date
long millis = calendar.getTimeInMillis(); // same as date.getTime()
Calendar other = Calendar.getInstance();
other.setTime(date); // Date -> Calendar
System.out.println(date);
System.out.println(millis == date.getTime());
System.out.println(other.get(Calendar.MONTH) + 1);
}
} Output (JVM time zone UTC):
Sun Oct 18 14:30:00 UTC 2026
true
10 getTime() returns a new Date object: later changes to the calendar through add() or set() do not affect it, and changing the Date does not affect the calendar.
3. add() vs roll(): changing a date
add(field, amount) adds a value to a field and carries the overflow into larger fields: 10 days after 26 November is 6 December. roll(field, amount) cycles only the given field within its range and leaves month and year untouched: the same 10 days after 26 November give 6 November.
import java.util.Calendar;
import java.util.GregorianCalendar;
public class CalendarExample1 {
public static void main(String[] args) {
// calendar for the current date
Calendar calendar1 = new GregorianCalendar();
System.out.println("Calendar for today: " + calendar1.getTime());
// calendar for 25 Dec 2013 (months from 0: DECEMBER == 11)
Calendar calendar2 = new GregorianCalendar(2013, Calendar.DECEMBER, 25);
calendar2.add(Calendar.DAY_OF_YEAR, 1); // plus 1 day
System.out.println("Calendar for 26 Dec 2013: " + calendar2.getTime());
calendar2.add(Calendar.MONTH, -1); // minus 1 month
System.out.println("Calendar for 26 Nov 2013: " + calendar2.getTime());
calendar2.roll(Calendar.DAY_OF_MONTH, 10);
System.out.println("Calendar after roll: " + calendar2.getTime());
Calendar calendar3 = new GregorianCalendar(2013, Calendar.NOVEMBER, 26);
calendar3.add(Calendar.DAY_OF_MONTH, 10);
System.out.println("Calendar after add: " + calendar3.getTime());
}
} Output (JVM time zone UTC; the first line depends on when you run it):
Calendar for today: Sun Oct 18 12:00:00 UTC 2026
Calendar for 26 Dec 2013: Thu Dec 26 00:00:00 UTC 2013
Calendar for 26 Nov 2013: Tue Nov 26 00:00:00 UTC 2013
Calendar after roll: Wed Nov 06 00:00:00 UTC 2013
Calendar after add: Fri Dec 06 00:00:00 UTC 2013 | Method | What it does | 26 Nov 2013 + 10 days | When to use it |
|---|---|---|---|
add(DAY_OF_MONTH, 10) | Adds with carry into month and year | 6 Dec 2013 | Normal date arithmetic: deadlines, due dates, "in one week" |
roll(DAY_OF_MONTH, 10) | Cycles the field within its own range | 6 Nov 2013 | Rarely: spinning a value in a UI widget where the month must not change |
set(DAY_OF_MONTH, 36) | Stores the value; in lenient mode it is normalized when read | 6 Dec 2013 | Setting a specific field rather than shifting it |
4. The Date class: a moment in milliseconds
The Date class holds a single long value — the number of milliseconds since 1970-01-01T00:00:00 UTC (the Unix epoch). The no-argument constructor new Date() captures the current moment, and getTime() returns that internal number of milliseconds.
| Constructor / method | What it does | Note |
|---|---|---|
new Date() | Creates an object for the current moment | Millisecond precision |
new Date(long ms) | The moment ms milliseconds after the epoch | A negative value means a date before 1970 |
getTime() | Returns milliseconds since the epoch | For new Date() equals System.currentTimeMillis() |
setTime(long ms) | Changes the stored moment | Date is mutable: everyone holding the reference sees the change |
before(), after(), compareTo(), equals() | Compare two dates | Comparison is by milliseconds, not by calendar day |
toInstant(), Date.from(Instant) | Bridge to java.time (since Java 8) | On java.sql.Date, toInstant() throws UnsupportedOperationException |
Example: using the Date class
import java.util.Date;
public class DateExample1 {
public static void main(String[] args) {
Date date1 = new Date();
System.out.println("Current date: " + date1);
System.out.println("Milliseconds: " + date1.getTime());
System.out.println();
Date date2 = new Date(12121212);
System.out.println("Date set: " + date2);
date2.setTime(34343434);
System.out.println("Date changed: " + date2);
}
} Output with the JVM time zone set to UTC (the first two lines depend on when you run it — here 18 October 2026, 12:00 UTC):
Current date: Sun Oct 18 12:00:00 UTC 2026
Milliseconds: 1792324800000
Date set: Thu Jan 01 03:22:01 UTC 1970
Date changed: Thu Jan 01 09:32:23 UTC 1970 12,121,212 ms is 3 hours 22 minutes 1.212 seconds after the epoch, and 34,343,434 ms is 9 hours 32 minutes 23.434 seconds. toString() does not print milliseconds.
Date has no time zone
A Date contains only milliseconds since the epoch in UTC. The time zone appears only when the date is printed: toString() uses the default zone of the JVM (TimeZone.getDefault()). The same new Date(12121212) prints as Thu Jan 01 06:22:01 MSK 1970 on a machine in Moscow and as 03:22:01 UTC on a UTC server. For predictable output, format the date with an explicitly set time zone instead of relying on toString().
5. Why new Date(2024, 1, 15) is the year 3924
The deprecated constructor Date(int year, int month, int date) takes the year as an offset from 1900 and the month as a number from 0 to 11. So new Date(2024, 1, 15) means 1900 + 2024 = the year 3924, and month 1 is February. The day of the month is counted normally, starting from 1.
import java.time.LocalDate;
import java.util.Date;
public class DateYear1900 {
public static void main(String[] args) {
Date wrong = new Date(2024, 1, 15); // deprecated
System.out.println(wrong);
System.out.println(wrong.getYear()); // year minus 1900
Date right = new Date(124, 1, 15); // 124 = 2024 - 1900, 1 = February
System.out.println(right);
System.out.println(right.getYear() + " " + right.getMonth());
System.out.println(LocalDate.of(2024, 2, 15));
}
} Output (JVM time zone UTC):
Fri Feb 15 00:00:00 UTC 3924
2024
Thu Feb 15 00:00:00 UTC 2024
124 1
2024-02-15 Look at the second line: getYear() returns 2024 for the year 3924. If your test checks only that method, the bug slips through.
Where 1900 comes from. Date appeared in Java 1.0 (1996) and copied the conventions of struct tm from the C standard library: tm_year is the number of years since 1900, and tm_mon is a number from 0 to 11, convenient as an index into an array of month names. That is why getYear() returns the year minus 1900 (2024 → 124) and getMonth() returns 0..11.
In Java 1.1 these constructors and methods (Date(int, int, int), getYear(), getMonth(), setHours() and others) were marked @Deprecated in favor of Calendar. Calendar dropped the 1900 offset but kept zero-based months: Calendar.JANUARY == 0. A human-friendly model — the year is the year, January is 1 — arrived only with java.time in Java 8.
6. Where developers get tripped up
Most bugs with Calendar and Date are not exceptions but silently wrong dates: a month off by one, a year off by 1900, 30 February quietly turning into 2 March. The compiler catches none of them, so it pays to know these cases in advance.
| Mistake | What happens | Fix |
|---|---|---|
new GregorianCalendar(2024, 3, 15) meant as "15 March" | You get 15 April: months start from 0 | Constants such as Calendar.MARCH, or LocalDate.of(2024, 3, 15) |
new Date(2024, 1, 15) | Year 3924 (offset from 1900) | LocalDate.of(2024, 2, 15), converted to Date if an old API needs it |
| 30 February in a Calendar | Lenient mode is on by default: the date silently becomes 2 March | setLenient(false) — then getTime() throws IllegalArgumentException |
set(Calendar.HOUR, 15) | HOUR is the 12-hour field used with AM_PM; 15 hours overflow with a carry | Calendar.HOUR_OF_DAY for 0..23 |
Android: month from DatePickerDialog used as is in a date | In onDateSet(view, year, month, dayOfMonth) the month is 0 to 11 — the date shifts by one month | Pass month unchanged to Calendar.set(), but use month + 1 for LocalDate.of() |
| Sharing one Calendar or Date between threads, returning it from a getter | Both classes are mutable and not thread-safe: other code can change your date | Return a copy (new Date(d.getTime()), calendar.clone()) or use immutable java.time classes |
java.sql.Date.toInstant() | UnsupportedOperationException: an SQL date has no time part | sqlDate.toLocalDate() |
Lenient and strict mode side by side:
import java.util.Calendar;
import java.util.GregorianCalendar;
public class LenientExample {
public static void main(String[] args) {
Calendar lenient = new GregorianCalendar(2023, Calendar.FEBRUARY, 30);
System.out.println(lenient.getTime());
Calendar strict = new GregorianCalendar();
strict.setLenient(false);
strict.clear();
strict.set(2023, Calendar.FEBRUARY, 30);
System.out.println(strict.getTime());
}
} Output (JVM time zone UTC):
Thu Mar 02 00:00:00 UTC 2023
Exception in thread "main" java.lang.IllegalArgumentException: ... February 2023 has 28 days, so in lenient mode "30 February" becomes 2 March without any warning. In java.time, LocalDate.of(2023, 2, 30) throws DateTimeException immediately.
7. Moving to java.time: converting Date and Calendar
Since Java 8, Date and Calendar have bridge methods to java.time: Date.toInstant() and Date.from(Instant), GregorianCalendar.toZonedDateTime() and GregorianCalendar.from(ZonedDateTime). Converting to LocalDate needs a time zone, because Date is a moment in time while LocalDate is a calendar day without a zone.
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.GregorianCalendar;
public class LegacyConversion {
public static void main(String[] args) {
ZoneId zone = ZoneId.systemDefault();
// Date <-> Instant
Date date = new Date();
Instant instant = date.toInstant();
Date fromInstant = Date.from(instant);
// Date <-> LocalDate (through a time zone)
LocalDate localDate = date.toInstant().atZone(zone).toLocalDate();
Date startOfDay = Date.from(localDate.atStartOfDay(zone).toInstant());
// GregorianCalendar <-> ZonedDateTime
GregorianCalendar calendar = new GregorianCalendar();
ZonedDateTime zdt = calendar.toZonedDateTime();
GregorianCalendar back = GregorianCalendar.from(zdt);
// java.sql.Date <-> LocalDate (toInstant() does not work here)
java.sql.Date sqlDate = java.sql.Date.valueOf(localDate);
LocalDate fromSql = sqlDate.toLocalDate();
}
} An overview of java.time classes — when to choose LocalDate, LocalDateTime, ZonedDateTime or Instant — is in the lesson Java Date-Time API (java.time).
Frequently asked questions
What is the difference between Date and Calendar in Java?
Date stores a single moment as milliseconds since 1 January 1970 UTC and has almost no date logic. Calendar splits that moment into fields such as year, month, day and hour using a time zone and locale, and lets you change the date with set(), add() and roll() — see add() vs roll().
How do I convert Calendar to Date and Date to Calendar in Java?
Calendar to Date: calendar.getTime(). Date to Calendar: Calendar.getInstance() and then calendar.setTime(date). Both objects hold the same moment in milliseconds, so nothing is lost — see the conversion example.
Is the Java Calendar class deprecated?
No. Neither java.util.Calendar nor java.util.Date is deprecated as a class. What is deprecated since Java 1.1 are the Date constructors and methods that work with date fields: Date(int, int, int), getYear(), getMonth(), setHours() and others. For new code the recommended API is java.time (Java 8); Calendar and Date remain for compatibility with older libraries.
Should I use Calendar or java.time on Android?
java.time is available natively from Android API level 26, and on older API levels through core library desugaring in the Android Gradle Plugin. Prefer LocalDate and ZonedDateTime for new code. Calendar is still common in older code and in DatePickerDialog callbacks, where the month arrives as 0 to 11 — see where developers get tripped up.
What is the difference between new Date().getTime() and System.currentTimeMillis()?
Both return the same number — the current time in milliseconds since the Unix epoch. System.currentTimeMillis() does not create an object, so it is simpler for timestamps. To measure how long code runs, System.nanoTime() is more precise.
How do I compare two Date objects in Java?
Use before(), after() and compareTo(), or compare the getTime() values. equals() returns true only if both dates match to the millisecond, so two dates on the same day with different times are not equal. To compare calendar days only, convert Date to LocalDate — see converting Date and Calendar.
Read also: NumberFormat in Java, Locale in Java, Free Java course, Java tests online.
Comments