Hi! Today we'll start working with a new data type we haven't encountered before, namely, dates.
I don't think I need to explain what a date is. :) In principle, we could store the current data 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 long
s, 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:
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.
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? :)