User Milan Vucic
Milan Vucic
Programming Tutor at Codementor.io

How not to get lost in time: DateTime and Calendar

Published in the Java Developer group
Hi! Today we'll start working with a new data type we haven't encountered before, namely, dates. How not to get lost in time: DateTime and Calendar - 1I don't think I need to explain what a date is. :) In principle, we could store the current date and time in an ordinary Java String.

public class Main {
   public static void main(String[] args) {

       String date = "June 11, 2018";
       System.out.println(date);
   }
}
But this approach has many drawbacks. The String class is designed to work with text, and its methods are appropriate for this task. If we need to manipulate a date in some way (add 2 hours, for example), String doesn't work so well. Or if we want to display the current date and time when the program is compiled. String doesn't help here either: by the time you write the code and run it, the time will have changed and the console will display the wrong information. That's why Java's creators provided several classes for working with dates and time. The first of these is java.util.Date

Date class

We specified its full name, because another Java package has the java.sql.Date class. Don't mix them up! The first thing you need to know about it is that it stores the date as the number of milliseconds that have passed since January 1, 1970. This time system even has its own name: "Unix-time" A rather interesting approach, wouldn't you agree? :) The second thing worth remembering is this: If you create a Date object using the default constructor, the result represents the current date and time at the moment the object was created. Remember that we said that a date represented as a String would struggle with such a task? The Date class handles it with ease.

public class Main {
   public static void main(String[] args) {

       Date date = new Date();
       System.out.println(date);
   }
}
Run this code several times, and you'll see the time change repeatedly. :) This is possible because the time is stored as milliseconds: they are extremely small units of time, so the results are highly accurate. The Date class another constructor: you can pass the exact number of milliseconds since 00:00 on January 1, 1970 to the required date, and a corresponding date object will be created:

public class Main {
   public static void main(String[] args) {

       Date date = new Date(1212121212121L);
       System.out.println(date);
   }
}
Console output: Fri May 30 04:20:12 GMT 2008 We get May 30, 2008. "Fri" indicates the day of the week (Friday, duh), and GMT is the time zone (Greenwich Mean Time). Milliseconds are passed as longs, because the number of milliseconds does not usually fit into an int. So, what operations with dates we might we need to perform? Well, the most obvious, of course, is comparison. To determine whether one date comes before or after another. This can be done in several ways. For example, you can call the Date.getTime() method, which returns the number of milliseconds that have elapsed since midnight on January 1, 1970. Just call it on two Date objects and compare the results:

public class Main {
   public static void main(String[] args) {

       Date date1 = new Date();

       Date date2 = new Date();

       System.out.println((date1.getTime() > date2.getTime())?
               "date1 is later than date2" : "date1 is earlier than date2");
   }
}
Output: date1 is earlier than date2 But there's also a more convenient way, i.e. by using special methods provided by the Date class: before(), after() and equals(). All of them return a boolean value. The before() method checks whether our date is earlier than the date passed as an argument:

public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// Suspend the program for 2 seconds
       Date date2 = new Date();

       System.out.println(date1.before(date2));
   }
}
Console output: true Similarly, the after() method checks to see if our date is later than the date passed as an argument:

public class Main {
   public static void main(String[] args) throws InterruptedException {

       Date date1 = new Date();

       Thread.sleep(2000);// Suspend the program for 2 seconds
       Date date2 = new Date();

       System.out.println(date1.after(date2));
   }
}
Console output: false In our examples, we "put the program to sleep" for 2 seconds, so that the two dates are guaranteed to be different. On fast computers, the time between the creation of date1 and date2 could be less than one millisecond, causing both before() and after() to return false. But in this case, the equals() method will return true! After all, it compares the number of milliseconds since 00:00 on January 1, 1970 for each date. The objects are considered equal only if they match to the millisecond:

public static void main(String[] args) {

   Date date1 = new Date();
   Date date2 = new Date();

   System.out.println(date1.getTime());
   System.out.println(date2.getTime());

   System.out.println(date1.equals(date2));
}
Here's another thing you need to pay attention to. If you open the documentation for the Date class on the Oracle website, you'll see that many of its methods and constructors have been marked as Deprecated (i.e. not recommended for use). Here's what Java's creators have to say about parts of classes that have been deprecated:
"A program element annotated @Deprecated is something programmers are not recommended to use, usually because it's dangerous, or because there is a better alternative."
This doesn't mean that these methods can't be used at all. If you try to run code using deprecated methods in an IDE, it will most likely work For example, consider the deprecated Date.getHours() method, which returns the number of hours associated with a Date object.

public static void main(String[] args) {

   Date date1 = new Date();

   System.out.println(date1.getHours());
}
If you start the code at 14:21 (2:21 PM), it will display the number 14. As you can see, the deprecated method is crossed out, but it still works. These methods aren't removed in order to not break the huge body of existing code that uses them. In other words, these methods are neither "broken" nor "removed". They are simply not recommended for use because a more convenient alternative is available. Incidentally, the documentation specifically mentions this alternative:
How not to get lost in time: DateTime and Calendar - 2
Most of the Date class's methods have been moved the improved and extended Calendar class. We'll get acquainted with that class next. :)

Calendar class

JDK 1.1 introduced a new class: Calendar. It made working with dates in Java somewhat easier than before. The only implementation of the class Calendar that we'll work with is the GregorianCalendar class. It implements the Gregorian calendar, which is observed by most countries of the world. Its main advantage is that it can work with dates in a more convenient format. For example, it can:
  • Add a month or day to the current date
  • Check whether the year is a leap year;
  • Return individual components of the date (for example, extract the month number from a whole date)
  • It also contains a very convenient system of constants (many of which we will see below).
Another important enhancement of the Calendar class is its Calendar.ERA constant: you can indicate a date before the common era (BC - before Christ) or in the common era (AD - Anno Domini). Let's look at all this with examples. Let's create a calendar object with the date of January 25, 2017:

public static void main(String[] args) {

  Calendar calendar = new GregorianCalendar(2017, 0 , 25);
}
In the Calendar class (as well as the Date class for that matter), months start from zero, so we pass the number 0 as the second argument. When working with the Calendar class, it's important to understand that this is just that, a calendar, not an individual date. How not to get lost in time: DateTime and Calendar - 3 A date is just a few numbers that indicate a specific time interval. A calendar is a whole system that lets you do a lot of things with dates. :) This is abundantly apparent if you try to display the Calendar object: Output: java.util.GregorianCalendar[time=?,areFieldsSet=false,areAllFieldsSet=false,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Europe/London",offset=0,dstSavings=0,useDaylight=false,transitions=79,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=1,ERA=?,YEAR=2017,MONTH=0,WEEK_OF_YEAR=?,WEEK_OF_MONTH=?,DAY_OF_MONTH=25,DAY_OF_YEAR=?,DAY_OF_WEEK=?,DAY_OF_WEEK_IN_MONTH=?,AM_PM=0,HOUR=0,HOUR_OF_DAY=0,MINUTE=0,SECOND=0,MILLISECOND=?,ZONE_OFFSET=?,DST_OFFSET=?] See how much information you get! A calendar has a bunch of properties that a normal date does not have, and all of them are displayed (this is how the toString() method works in the Calendar class). If you just need to get a simple date from the calendar, i.e. a Date object, use the Calendar.getTime() method (the name isn't the most logical, but what can you do?):

public static void main(String[] args) {

   Calendar calendar = new GregorianCalendar(2017, 0 , 25);
   Date date = calendar.getTime();
   System.out.println(date);
}
Output: Wed Jan 25 00:00:00 GMT 2017 Now we have taken the calendar and "reduced it" to an ordinary date. Let's go further. In addition to designating months by their number, you can use the Calendar class's constant field values. These constants are static fields of the Calendar class with a preset value that cannot be changed. This is actually an even better option, because using them improves the readability of your code.

public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
}
Calendar.JANUARY is one of the constants that represent the months of the year. Using these named constants, no one will forget, for example, that the number 3 means April, and not the third month, which we like to call March. Just write Calendar.APRIL, and you're done. :) All calendar fields (number, month, minutes, seconds, etc.) can be specified separately using the set() method. This method is very convenient, because the Calendar class has a constant for each field, and the resulting code is very easy to read. In the last example, we created a date, but did not set a time for it. Let's set the time 19:42:12

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar();
   calendar.set(Calendar.YEAR, 2017);
   calendar.set(Calendar.MONTH, 0);
   calendar.set(Calendar.DAY_OF_MONTH, 25);
   calendar.set(Calendar.HOUR_OF_DAY, 19);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   System.out.println(calendar.getTime());
}
Output: Wed Jan 25 19:42:12 GMT 2017 We call the set() method, passing a constant (depending on the field we want to change) and the new value for the field. It turns out that this set() method is a kind of "super-setter" that knows how to set the value not just for one field, but for many fields. :) The Calendar class uses the add() method to add and subtract values. You pass in the field you want to change, and a number (exactly how much you want to add/subtract from the current value). For example, let's get a date that is 2 months before the date we created:

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 19);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.add(Calendar.MONTH, -2); // To subtract, pass a negative number
   System.out.println(calendar.getTime());
}
Output: Fri Nov 25 19:42:12 GMT 2016 Very good! We got the date 2 months ago. This didn't only cause the month to change: the year also changed from 2017 to 2016. Of course, when converting dates, the current year is calculated automatically without any need for you to keep track of it manually. But if for some reason you need to disable this behavior, you can do so. The roll() method can add and subtract values without affecting the remaining values. For example, like this:

public static void main(String[] args) {
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.roll(Calendar.MONTH, -2);
   System.out.println(calendar.getTime());
}
We did exactly the same thing as in the previous example: we took 2 months from the current date. But now the code does something different: the month has changed from January to November, but the year remains unchanged—2017! Output: Sat Nov 25 10:42:12 GMT 2017 Moving along. As we said above, we can get all the Calendar fields separately. We do this with the get() method:

public static void main(String[] args) {
   GregorianCalendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   System.out.println("Year: " + calendar.get(Calendar.YEAR));
   System.out.println("Month: " + calendar.get(Calendar.MONTH));
   System.out.println("Week in the month: " + calendar.get(Calendar.WEEK_OF_MONTH));// Week in this month?

   System.out.println("Day: " + calendar.get(Calendar.DAY_OF_MONTH));

   System.out.println("Hours: " + calendar.get(Calendar.HOUR));
   System.out.println("Minutes: " + calendar.get(Calendar.MINUTE));
   System.out.println("Seconds: " + calendar.get(Calendar.SECOND));
   System.out.println("Milliseconds: " + calendar.get(Calendar.MILLISECOND));

}
Output: Year: 2017 Month: 0 Week in the month: 5 Day: 25 Hours: 10 Minutes: 42 Seconds: 12 Milliseconds: 0 So, in addition to the Calendar class's "super-setter", there is also a "super-getter". :) Of course, another interesting aspect of this class is working with eras. To create a "BC" date, you'll need to use the Calendar.ERA field For example, let's create a date for the Battle of Cannae, where Hannibal defeated the Roman army. This happened on August 2, 216 BC:

public static void main(String[] args) {
   GregorianCalendar cannae = new GregorianCalendar(216, Calendar.AUGUST, 2);
   cannae.set(Calendar.ERA, GregorianCalendar.BC);

   DateFormat df = new SimpleDateFormat("MMM dd, yyy GG");
   System.out.println(df.format(cannae.getTime()));
}
Here we used the SimpleDateFormat class to print the date in a format that's easier for us to understand (the letters "GG" indicate we want the era to be displayed). Output: Aug. 02, 216 BC. The Calendar class has many more methods and constants. You can read about them in the documentation. If don't like this date format Sat Nov 25 10:42:12 GMT 2017 then you can use SimpleDateFormat to easily make it what you want it to be.

public static void main(String[] args) {

   SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MMMM d, yyyy");
   Calendar calendar = new GregorianCalendar(2017, Calendar.JANUARY , 25);
   calendar.set(Calendar.HOUR, 10);
   calendar.set(Calendar.MINUTE, 42);
   calendar.set(Calendar.SECOND, 12);

   calendar.roll(Calendar.MONTH, -2);
   System.out.println(dateFormat.format(calendar.getTime()));
}
Output: Saturday, November 25, 2017 That's much better, isn't it? :)
Comments (27)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
龚凌江 Level 11, China, China
25 December 2021
过时了
carlos oliveira Level 10, Seattle, United States
28 November 2021
That's Cool!!!!
Łukasz Wiśniewski Level 9, Katowice, Polska
25 November 2021
I think this article is a little outdated. There is now a new way to deal with dates and time: Date-Time API. Please check the documentation from Oracle to see all the changes.
Piotr Lelonek Level 24, Warsaw, Poland
10 August 2020
Zauważyłem, że im dalej w las tym gorzej z tłumaczeniem na język polski.... Warto byłoby dopracować artykuły, bo czasami ciężko zrozumieć co autor miał na myśli...
Hannah Level 11, Koblenz, Germany
30 July 2020
I am currently working on one of your exercises and somehow the Calendar.MONTH isn't working. The Compiler is telling me, that there is no constructor for GregorianCalendar(int,java.lang.String,int). Only java.util.GregorianCalendar.GregorianCalendar(int,int,int). So I guess I will have to write a little method to transform the Month-Word into the corresponding number... Maybe someone can tell me, why Calendar.MONTH isn't working? Am I needed to import another class? Currently I have util.Date; util.Calendar and util.GregorianCalendar;
Thang Za Thang Level 18, Melbourne, Australia
25 July 2020
This stuff is what we need at the start of the level, not now where we can't go back and re-do tasks.
WIDMO Level 41, Gdańsk, Poland
14 April 2020
I was wondering why this code wasn't working for me ?

public class Main {
   public static void main(String[] args) {

       Date date1 = new Date();

       Date date2 = new Date();

       System.out.println((date1.getTime() > date2.getTime())?
               "date1 is later than date2" : "date1 is earlier than date2");
   }
}
For those who has r asking the same question,anwer is simple. There sould be a code taking some time between those declarations:

       Date date1 = new Date();
       Date date2 = new Date();
( i.e. thread.sleep(1000); ) If there is a space then long numbers which are representation of dates in miliseconds would be just the same :)
Ashish RajAnand Level 13, Bhilai , India
13 April 2020
nice news about calendar class. I called news because read first time.
David Level 29, Paris, France
28 March 2020
Very Good
Thomas Sixberry Level 16, Rochester Hills, United States
31 January 2020
Thanks for opening my noob eyes to the oracle website. ahahaha