Working with dates and times without the proper tools can be a
chore.[2]
Java 1.1 gives you three classes that do all the hard work for you.
The java.util.Date class encapsulates a point
in time. The java.util.GregorianCalendar class,
which descends from the abstract java.util.Calendar,
translates
between a point in time and calendar fields like month, day, and
year. Finally, the java.text.DateFormat class
knows how to generate and parse string representations of dates
and times. In Java 1.0.2,
the Date class performed all three functions. In
Java 1.1, most of its methods have been deprecated, so that the only purpose
of the Date class is to represent a point in time.
[2] For a wealth of information about time and world time-keeping conventions, see the web site http://tycho.usno.navy.mil/, the U.S. Navy Directorate of Time. For a fascinating history of the Gregorian and Julian calendars, try http://www.magnet.ch/serendipity/hermetic/cal_stud/cal_art.htm.
The separation of the
Date class and the
GregorianCalendar class is analogous to having
a class representing temperature and a class that translates that temperature
to Celsius units. Conceivably, we could define other subclasses of
Calendar, say
JulianCalendar or
LunarCalendar.
The default GregorianCalendar constructor creates an
object that represents the current time, as determined by the system
clock:
GregorianCalendar now = new GregorianCalendar();
Other constructors accept values to initialize the calendar. In the first statement below, we construct an object representing August 9, 1996; the second statement specifies both a date and a time, yielding an object that represents 9:01 a.m., April 8, 1997.
GregorianCalendar daphne =
new GregorianCalendar(1996, Calendar.AUGUST, 9);
GregorianCalendar sometime =
new GregorianCalendar(1997, Calendar.APRIL, 8, 9, 1); // 9:01 AMWe can also create a GregorianCalendar
by setting specific
fields using the
set() method. The
Calendar class contains a torrent
of constants representing both calendar fields and field values. The
first argument to the set() method
is a field constant; the second argument is the new value for the field.
GregorianCalendar kristen = new GregorianCalendar(); kristen.set(Calendar.YEAR, 1972); kristen.set(Calendar.MONTH, Calendar.MAY); kristen.set(Calendar.DATE, 20);
A GregorianCalendar is created
in the default time zone. Setting the time zone of the calendar is as
easy as obtaining the desired TimeZone and
giving it to the GregorianCalendar:
GregorianCalendar smokey = new GregorianCalendar();
smokey.setTimeZone(TimeZone.getTimeZone("MST"));
To create a string representing a point in time, use the
DateFormat class. Although
DateFormat itself is abstract,
it has several factory methods that return useful
DateFormat subclass instances.
To get a default DateFormat, simply
call getInstance():
DateFormat plain = DateFormat.getInstance(); String now = plain.format(new Date()); // 4/9/97 6:06 AM
Those of you who don't live on the West coast of North America will notice that
the example above produces a result that is not quite right.
DateFormat instances stubbornly
insist on using Pacific Standard Time, so you have to tell them
what time zone you're in:
DateFormat plain = DateFormat.getInstance(); plain.setTimeZone(TimeZone.getDefault()); String now = plain.format(new Date()); // 4/9/97 9:06 AM
You can generate a date string or a time string, or both,
using the
getDateInstance(),
getTimeInstance(), and
getDateTimeInstance()
factory
methods. The argument to these methods describes
what level of detail you'd like to see.
DateFormat
defines four constants representing detail levels: they are
SHORT,
MEDIUM,
LONG, and
FULL. There is also
a DEFAULT, which is
the same as MEDIUM. The code below creates
three DateFormat instances: one to format
a date, one to format a time, and one to format a date and time
together. Note that getDateTimeInstance()
requires two arguments: the first specifies how to format the date,
the second says how to format the time:
DateFormat df = DateFormat.getDateInstance(DateFormat.DEFAULT); // 09-Apr-97
DateFormat tf = DateFormat.getTimeInstance(DateFormat.DEFAULT); // 9:18:27 AM
DateFormat dtf = // Wednesday, April 09, 1997 9:18:27 o'clock AM EDT
DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
Formatting dates and times for other countries is just as easy. Overloaded factory methods accept a
Locale argument:
DateFormat df = // 9 avr. 97
DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.FRANCE);
DateFormat tf = // 9:27:49
DateFormat.getTimeInstance(DateFormat.DEFAULT, Locale.GERMANY);
DateFormat dtf = // mercoledi 9 aprile 1997 9.27.49 GMT-04:00
DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, Locale.ITALY);
To parse a string representing a date, we use the
parse() method of the
DateFormat class. The result is a
Date object. The parsing
algorithms are finicky, so it's
safest to parse dates and times that are in the same format that
is produced by the DateFormat. The
parse() method throws
a ParseException if it doesn't understand
the string you give it. All the calls to parse() below succeed except the last; we don't supply a time zone, but the format for the time is LONG. Other exceptions are occasionally thrown
from the parse() method. To cover
all the bases, catch NullPointerExceptions
and StringIndexOutOfBoundsExceptions also.
try {
Date d;
DateFormat df;
df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
d = df.parse("Wednesday, April 09, 1997 2:22:22 o'clock PM EST");
df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
d = df.parse("09-Apr-97 2:22:22 PM");
df = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
d = df.parse("April 09, 1997 2:22:22 PM EST");
d = df.parse("09-Apr-97 2:22:22 PM");//ParseException; detail level mismatch
}
catch (Exception e) {}
There's been a lot of talk lately about the millenium bug.
This refers
to the expected failure of some software in the year 2000,
when programs that use two digits to represent years "roll over" and
interpret "00" as 1900 instead of 2000.
Java is mostly safe from this error. The
Date class has no specific
field for year and is thus immune to this problem. Internally, Java
keeps track of time in milliseconds since some base date, which is
fundamentally safe; the field used to hold the current count won't
overflow for a very long time.
The only time you could run into this error in Java is when you
use a DateFormat to parse a date string
with a two-digit year. Two-digit years are automatically prefixed
with 19 for the current century. My advice is to use a four-digit
year when you expect to parse a date string.