Intermediate Java programming https://www6.software.ibm.com/developerworks/education/j-intermed/se...
Intermediate Java programming
Dates
Introduction
The Java language gives you lots of tools for handling dates. Some of them are much more frustrating than the tools
available in other languages. That said, with the tools that the Java language provides, there is almost nothing you can't
do to create dates and format them exactly how you want.
Creating dates
When the Java language was young, it contained a class called Date that was quite helpful for creating and
manipulating dates. Unfortunately, that class did not support internationalization very well, so Sun added two classes
that aimed to help the situation:
Calendar
DateFormat
We'll talk about Calendar first, and leave DateFormat for later.
Creating a Date is still relatively simple:
Date aDate = new Date(System.currentTimeMillis());
Or we could use this code:
Date aDate = new Date();
This will give us a Date representing the exact date and time right now, in the current locale format.
Internationalization is beyond the scope of this tutorial, but for now, simply know that the Date you get back is
consistent with the geography of your local machine.
Now that we have an instance, what can we do with it? Very little, directly. We can compare one Date with another to
see if the first is before() or after() the second. We also can essentially reset it to a new instant in time by
calling setTime() with a long representing the number of milliseconds since midnight on January 1, 1970 (which is
what System.currentTimeMillis() returns). Beyond that, we're limited.
Calendars
The Date class is now more confusing than useful, because most of its date processing behavior is deprecated. You
used to be able to get and set parts of the Date (such as the year, the month, etc.). Now we're left having to use both
Date and Calendar to get the job done. Once we have a Date instance, we can use a Calendar to get and set
parts of it. For example:
Date aDate = new Date(System.currentTimeMillis());
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(aDate);
Here we create a GregorianCalendar and set its time to the Date we created before. We could have
accomplished the same goal by calling a different method on our Calendar:
1 of 6 9/18/2009 11:19 AM
Intermediate Java programming https://www6.software.ibm.com/developerworks/education/j-intermed/se...
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
Armed with a Calendar, we can now access and manipulate components of our Date. Getting and setting parts of
the Date is a simple process. We simply call appropriate getters and setters on our Calendar, like this:
calendar.set(Calendar.MONTH, Calendar.JULY);
calendar.set(Calendar.DAY_OF_MONTH, 15);
calendar.set(Calendar.YEAR, 1978);
calendar.set(Calendar.HOUR, 2);
calendar.set(Calendar.MINUTE, 15);
calendar.set(Calendar.SECOND, 37);
System.out.println(calendar.getTime());
This will print the formatted output string for July 15, 1978 at 02:15:37 a.m. (there also are helper methods on
Calendar that allow us to set some or almost all of those components simultaneously). Here we called set(),
which takes two parameters:
The field (or component) of the Date we want to set.
The value for that field.
We can reference the fields with named constants in the Calendar class itself. In some cases, there is more than one
name for the same field, as with Calendar.DAY_OF_MONTH, which can also be referenced with Calendar.DATE.
The values are straightforward, except perhaps the ones for Calendar.MONTH and the one for Calendar.HOUR.
Months in Java language dates are zero-based (that is, January is 0), which really makes it wise to use the named
constants to set them, and can make it frustrating to display dates correctly. The hours run from 0 to 24.
Once we have an established Date, we can extract parts of it:
System.out.println("The YEAR is: " + calendar.get(Calendar.YEAR));
System.out.println("The MONTH is: " + calendar.get(Calendar.MONTH));
System.out.println("The DAY is: " + calendar.get(Calendar.DATE));
System.out.println("The HOUR is: " + calendar.get(Calendar.HOUR));
System.out.println("The MINUTE is: " + calendar.get(Calendar.MINUTE));
System.out.println("The SECOND is: " + calendar.get(Calendar.SECOND));
System.out.println("The AM_PM indicator is: " + calendar.get(Calendar.AM_PM));
Built-in date formatting
You used to be able format dates with Date. Now you have to use several other classes:
DateFormat
SimpleDateFormat
DateFormatSymbols
We won't cover all the complexities of date formatting here. You can explore these classes on your own. But we will
talk about the basics of using these tools.
The DateFormat class lets us create a locale-specific formatter, like this:
DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT);
Date aDate = new Date();
String formattedDate = dateFormatter.format(today);
This code creates a formatted date string with the default format for this locale. On my machine, it looks something like
2 of 6 9/18/2009 11:19 AM
Intermediate Java programming https://www6.software.ibm.com/developerworks/education/j-intermed/se...
this:
Nov 11, 2005
This is the default style, but it's not all that is available to us. We can use any of several predefined styles. We also
can call DateFormat.getTimeInstance() to format a time, or DateFormat.getDateTimeInstance()
to format both a date and a time. Here is the output of the various styles, all for the U.S. locale:
Style Date Time Date/Time
DEFAULT Nov 11, 2005 7:44:56 PM Nov 11, 2005 7:44:56 PM
SHORT 11/11/05 7:44 PM 11/11/05 7:44 PM
MEDIUM Nov 11, 2005 7:44:56 PM Nov 11, 2005 7:44:56 PM
LONG November 11, 2005 7:44:56 PM EST November 11, 2005 7:44:56 PM EST
FULL Thursday, November 11, 2005 7:44:56 PM EST Thursday, November 11, 2005 7:44:56 PM EST
Customized formatting
These predefined formats are fine in most cases, but you can also use SimpleDateFormat to define your own
formats. Using SimpleDateFormat is straightforward:
Instantiate a SimpleDateFormat with a format pattern string (and a locale, if you wish).
Call format() on it with a Date.
The result is a formatted date string. Here's an example:
Date aDate = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
String formattedDate = formatter.format(today);
System.out.println(formattedDate);
When you run this code, you'll get something like the following (it will reflect the date that's current when you run the
code, of course):
11/05/2005
The quoted string in the example above follows the pattern syntax rules for date formatting patterns. Java.sun.com has
some excellent summaries of those rules (see Resources). Here are some helpful rules of thumb:
You can specify patterns for dates and times.
Some of the pattern syntax isn't intuitive (for example, mm defines a two-digit minute pattern; to get an
abbreviated month, you use MM).
You can include text literals in your patterns by placing them in single quotes (for example., using "'on'
MM/dd/yyyy" above produces on 11/05/2005).
The number of characters in a text component of a pattern dictates whether its abbreviated or long form will be
3 of 6 9/18/2009 11:19 AM
Intermediate Java programming https://www6.software.ibm.com/developerworks/education/j-intermed/se...
used ("MM" yields 11, but "MMM" yields Nov, and "MMMM" yields November).
The number of characters in a numeric component of a pattern dictates the minimum number of digits.
If the standard symbols of SimpleDateFormat still don't meet your custom formatting needs, you can use
DateFormatSymbols to customize the symbols for any component of a Date or time. For example, we could
implement a unique set of abbreviations for months of the year, like this (using the same SimpleDateFormat as
before):
DateFormatSymbols symbols = new DateFormatSymbols();
String[] oddMonthAbbreviations = new String[] {
"Ja","Fe","Mh","Ap","My","Jn","Jy","Au","Se","Oc","No","De" };
symbols.setShortMonths(oddMonthAbbreviations);
formatter = new SimpleDateFormat("MMM dd, yyyy", symbols);
formattedDate = formatter.format(now);
System.out.println(formattedDate);
This code calls a different constructor on SimpleDateFormat, one that takes a pattern string and a
DateFormatSymbols that defines the abbreviations used when a short month appears in a pattern. When we format
the date with these symbols, the result looks something like this for the Date we saw above:
No 15, 2005
The customization capabilities of SimpleDateFormat and DateFormatSymbols should be enough to create any
format you need.
Manipulating dates
You can go forward and backward in time by incrementing and decrementing dates, or parts of them. Two methods let
you do this:
add()
roll()
The first lets you add some amount (or subtract by adding a negative amount) of time to a particular field of a Date.
Doing that will adjust all other fields of the Date accordingly based on the addition to a particular field. For example,
assume we begin with November 15, 2005 and increment the day field by 20. We could use code like this:
Calendar calendar = GregorianCalendar.getInstance();
calendar.set(Calendar.MONTH, 10);
calendar.set(Calendar.DAY_OF_MONTH, 15);
calendar.set(Calendar.YEAR, 2005);
formatter = new SimpleDateFormat("MMM dd, yyyy");
System.out.println("Before: " + formatter.format(calendar.getTime()));
calendar.add(Calendar.DAY_OF_MONTH, 20);
System.out.println("After: " + formatter.format(calendar.getTime()));
The result looks something like this:
Before: Nov 15, 2005
After: Dec 05, 2005
Rather simple. But what does it mean to roll a Date? It means you are incrementing or decrementing a particular
date/time field by a given amount, without affecting other fields. For example, we could roll our date from November
4 of 6 9/18/2009 11:19 AM
Intermediate Java programming https://www6.software.ibm.com/developerworks/education/j-intermed/se...
to December like this:
Calendar calendar = GregorianCalendar.getInstance();
calendar.set(Calendar.MONTH, 10);
calendar.set(Calendar.DAY_OF_MONTH, 15);
calendar.set(Calendar.YEAR, 2005);
formatter = new SimpleDateFormat("MMM dd, yyyy");
System.out.println("Before: " + formatter.format(calendar.getTime()));
calendar.roll(Calendar.MONTH, true);
System.out.println("After: " + formatter.format(calendar.getTime()));
Notice that the month is rolled up (or incremented) by 1. There are two forms of roll():
roll(int field, boolean up)
roll(int field, int amount)
We used the first. To decrement a field using this form, you pass false as the second argument. The second form of
the method lets you specify the increment or decrement amount. If a rolling action would create an invalid date value
(for example, 09/31/2005), these methods adjust the other fields accordingly, based on valid maximum and minimum
values for dates, hours, etc. You can roll forward with positive values, and backward with negative ones.
It's fine to try to predict what your rolling actions will do, and you can certainly do so, but more often than not, trial and
error is the best method. Sometimes you'll guess right, but sometimes you'll have to experiment to see what produces
the correct results.
Using Dates
Everybody has a birthday. Let's add one to our Person class. First, we add an instance variable to Person:
protected Date birthdate = new Date();
Next, we add accessors for the variable:
public Date getBirthdate() {
return birthdate;
}
public void setBirthdate(Date birthday) {
this.birthdate = birthday;
}
Next, we'll remove the age instance variable, because we'll now calculate it. We also remove the setAge()
accessor, because age will now be a derived value. We replace the body of getAge() with the following code:
public int getAge() {
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(new Date());
int currentYear = calendar.get(Calendar.YEAR);
calendar.setTime(birthdate);
int birthYear = calendar.get(Calendar.YEAR);
return currentYear - birthYear;
}
In this method, we now calculate the value of age based on the year of the Person's birthdate and the year of
today's date.
5 of 6 9/18/2009 11:19 AM
Intermediate Java programming https://www6.software.ibm.com/developerworks/education/j-intermed/se...
Now we can try it out, with this code:
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(new Date());
calendar.set(Calendar.YEAR, 1971);
calendar.set(Calendar.MONTH, 2);
calendar.set(Calendar.DAY_OF_MONTH, 23);
Adult anAdult = new Adult();
anAdult.setBirthdate(calendar.getTime());
System.out.println(anAdult);
We set birthdate on an Adult to March 23, 1971. If we run this code in January 2005, we should get this output:
An Adult with:
Age: 33
Name: firstname lastname
Gender: MALE
Progress: 0
There are a few other housekeeping details that I leave as an exercise for you:
Update compareTo() on Adult to reflect the presence of a new instance variable.
Had we implemented it, we would have to update equals() on Adult to reflect the presence of a new
instance variable.
Had we implemented equals(), we would have implemented hashCode() as well, and we would have to
update hashCode() to reflect the presence of a new instance variable.
6 of 6 9/18/2009 11:19 AM