Making Sense of Java's Dates
Pages: 1, 2
Additional Functionality
Implementation Leakage It has to be said that implementation details have been oozing into the
APIs to an uncommon degree for the "new" date-handling classes. Up to a point,
this is a reflection of their intended use as base classes for customized
development, but it also seems to occasionally be a consequence of insufficient
clarity in the design of the public interfaces. Whether the
|
The additional functions offered by the Calendar base class
fall into three categories. There are several static factory methods to obtain
instances initialized for arbitrary time zones and locales. As mentioned above,
all instances obtained this way have already been initialized to the
current time. No factory methods are provided to obtain a
Calendar instance initialized to an arbitrary point in time.
The second group of methods consists of the methods before( Object
) and after( Object ). They take arguments of type
Object, thus allowing these methods to be overridden in subclasses
for arbitrary types of arguments.
Finally, there are a number of functions to get and set additional properties, such as the current time zone. Among them are several methods that query the possible and actual minimum and maximum values of certain fields for the current calendar implementation.
When Does the Week Begin? The documentation on the |
java.util.GregorianCalendar
The class GregorianCalendar is the only commonly available
subclass of Calendar. It provides an implementation of the basic
Calendar abstraction suitable for the interpretation of dates
according to the conventions used commonly in the West. It adds a number of
public constructors, as well as some functions specific to Gregorian Calendars,
such as isLeapYear().
java.util.TimeZone and java.util.SimpleTimeZone
The TimeZone class and its subclasses are auxiliary classes,
required by Calendar to interpret dates according to the
selected time zone. Semantically, a time zone specifies a certain offset to be
added to GMT to reach the local time. Clearly, this offset changes when
daylight savings time (DST) is in effect. The TimeZone abstraction
therefore needs to keep track not only of the additional offset to be applied
if DST is in effect, but also of the rules that determine when DST is
in effect, in order to calculate the local time for any given date and
time.
The abstract base class TimeZone provides basic methods to
handle "raw" (without taking DST into account) and actual offsets (in
milliseconds!), but implementation of any functionality related to DST rules is
left to subclasses, such as SimpleTimeZone. The latter class
provides several ways to specify rules controlling the beginning and ending of
DST, such as a giving an explicit day in a month or a certain weekday following
a given date. Each TimeZone also has a human-readable,
locale-dependent display name. Display names come in two styles:
LONG and SHORT.
Time zones are unambiguously determined by an identifier string. The base
class provides the static method String[] getAvailableIDs() to obtain
all installed "well-known" standard time zones. (There are 557 for my
installation, using JDK 1.4.1.) The JavaDoc defines the proper syntax to build
custom time zone identifiers, if the need arises. Also provided are static
factory methods, to obtain TimeZone instances — either for a
specific ID or the default for the current location.
SimpleTimeZone also provides some public constructors and,
surprisingly for an abstract class, so does TimeZone. (The
JavaDoc states: "For invocation by subclass constructors." Apparently, it
should have been declared protected.)
java.text.DateFormat
While Calendar and related classes handle the locale-specific
interpretation of dates, the DateFormat classes assist
with the transformation of dates to and from human-readable strings. When
representing points in time, an additional localization issue arises: not only
the language, but also the date format is locale-dependent
(U.S.: Month/Day/Year, Germany: Day.Month.Year, etc.). The
DateFormat utility tries to manage these differences for the
application programmer.
The abstract base class DateFormat does not require (and does
not permit) the definition of arbitrary, programmer-defined date formats.
Instead, it defines four different format styles: SHORT,
MEDIUM, LONG, and FULL (in increasing
order of verbosity). Given a locale and a style, the programmer can rely on the
class to use an appropriate date format.
The abstract base class DateFormat does not define static
methods for formatting (date -> text) or parsing (text -> date). Instead, it
defines several static factory methods to obtain instances (of concrete
subclasses) initialized for a given locale and a chosen style. Since the
standard formats always include both date and time, additional factory
methods are available to obtain instances treating only the time or
date part. The String format( Date ) and Date parse( String
) methods then perform the transformation. Note that concrete subclasses
may choose to break this idiom.
The Calendar object used internally to interpret dates is
accessible and can be modified, as are the employed TimeZone and
NumberFormat objects. However, the locale and style can no longer
be changed once the DateFormat has been instantiated.
Also available are (abstract) methods for piece-wise parsing or formatting,
taking an additional ParsePosition or FieldPosition
argument, respectively. There are two versions for each of these methods. One
takes or returns a Date instance and the other takes or returns a
general Object, to allow handling of alternatives to
Date in subclasses. The class defines several public static
variables with names ending in _FIELD to identify the various
possible fields for use with FieldPosition (cf. the JavaDoc
for java.util.Format).
The only commonly available concrete subclass of DateFormat is
SimpleDateFormat. It provides all of the aforementioned
functionality, additionally allowing the definition of arbitrary date-formatting patterns. There is a rich syntax to specify formatting patterns; the
JavaDoc gives the full details. The pattern can be specified as an argument to
the constructors of this class or set explicitly.
Printing a Timestamp: A Cut-and-Paste ExampleImagine you want to print the current time in a user-defined format; for instance, to a log file. Here is how to do this:
Note the |
The Classes in java.sql.*
The date-and-time-handling classes in the java.sql.* all
extend java.util.Date. The fact that there are three of them
reflects the need to model the three standard SQL92 types DATE,
TIME, and TIMESTAMP.
Like java.util.Date, all three classes in the SQL package are
thin wrappers around a numeric value representing a point in time. The
Date and Time classes ignore the information
regarding the time of day or the calendar date, respectively.
The Timestamp class, however, not only includes the usual time
and date information up to millisecond precision, but also allows storing
additional data to accurately represent a point in time with nanosecond
precision. (A nanosecond is a billionth of a second.)
Besides shadowing the corresponding SQL datatypes, these classes handle
transformations to and from SQL-conforming String representations.
To this end, each of the three classes overrides the toString()
method. Furthermore, each class provides a static factory method,
valueOf( String ), which returns an instance of the class that it
has been invoked on, initialized to the time value represented by the
String passed to it. The format of the String
representation for all of these methods is fixed by the SQL standard and cannot be
changed by the programmer.
The additional data required to store nanosecond information has not been
very well integrated with the rest of the data representing the usual time and
date information in the Timestamp class. For example, calling
getTime() on a Timestamp instance will return the
number of milliseconds since the start of the Unix epoch, ignoring the
nanosecond data. Similarly, according to the JavaDoc, the
hashCode() method has not been overridden in the subclass, and
therefore also ignores the nanosecond data.
The JavaDoc for java.sql.Timestamp states that the
"inheritance relationship (...) really denotes implementation inheritance, and
not type inheritance," but even this statement is incorrect, since Java has no
notion of private (i.e. implementation) inheritance. Instead of inheriting
from java.util.Date, all of the classes in the
java.sql.* package should have been designed to encapsulate
a java.util.Date object, exposing only the methods required
— at the very least, methods such as hashCode() should have
been properly overridden.
A final comment concerns the handling of time zones by the database engine.
The classes in the java.sql.* package do not allow one to specify the
intended time zone explicitly. Database servers (or drivers) are free to
interpret this information as being valid in the server's local time zone, which
may be subject to change (for instance, due to daylight savings time).
Summary
From the foregoing discussion it should be clear that Java's date-handling
classes are not just complicated, but also poorly designed. Encapsulation is
leaky, the APIs are baroque and not well-organized, and uncommon idioms are
employed frequently for no good reason. The implementation holds additional
surprises (I suggest a look at the actual type of the object returned from
Calendar.getInstance( Locale ) for all available locales!) On
the other hand, the classes manage to treat all of the difficulties inherent in
internationalized date handling and, in any case, are here to stay. I hope
that this article was a little contribution in helping to clarify their proper
usage.
Call Me By My True NamesAs a last example of the wonderful consistency and orthogonality of Java's APIs, I would like to list three (maybe there are more!) different methods to obtain the number of milliseconds since the start of the Unix epoch:
|
Acknowledgements
The author would like to thank Wilhelm Fitzpatrick (Seattle) for a careful reading of the manuscript and valuable comments.
References
- International Calendars in Java at IBM : A detailed white paper by one of the original authors on the genesis and intended usage of Java's date-handling classes. Highly recommended.
- IBM alphaWorks:
International Calendars: Additional subclasses of
Calendarfor Buddhist, Hebrew, Muslim, and Japanese calendars used to be available at IBM's alphaWorks. Unfortunately, they seem to be temporarily unavailable. - Reingold on Calendars: Web site of Edward M. Reingold, author of Calendrical Calculations, the standard reference on calendars.
- About the Calendars: A brief overview of some of the more common international calendars.
- Thread on JavaLobby: A brief, but interesting, thread on JavaLobby. Apparently, some people considered the APIs of Java's date classes to be so bad that they filed an official bug-report to have them changed. Unfortunately, the request has been rejected.
Philipp K. Janert is a software project consultant, server programmer, and architect.
Return to ONJava.com
-
Updated Link
2005-08-05 07:06:18 uslickc [View]
-
Joda-Time
2005-02-22 16:01:12 jodastephen [View]
-
using date and time object
2005-02-01 11:02:40 merzee [View]
-
Scary stuff
2003-06-09 16:41:03 Dave Rolsky |
[View]
-
Is this a bug?
2003-06-08 21:08:54 anonymous2 [View]
-
Java date handling sucks
2003-06-05 21:01:24 anonymous2 [View]
-
Java date handling sucks
2003-06-05 21:07:08 anonymous2 [View]
-
Java date handling sucks
2003-06-18 21:10:22 anonymous2 [View]
-
Corrections Made
2003-06-05 09:08:42 chromatic |
[View]
-
Code Error
2003-06-05 07:39:41 anonymous2 [View]
-
Code Error
2003-06-05 08:42:31 anonymous2 [View]
-
The Horror!
2003-06-05 04:54:32 jimothy [View]
-
Minor correction
2003-06-05 01:28:26 anonymous2 [View]
-
badly designed, over-complicated garbage
2008-04-30 16:04:00 Anonymous8888 [View]