The following program exercises some basic features of the
Date and Calendar classes. What does it print?
import java.util.*; public class DatingGame { public static void main(String[] args) { Calendar cal = Calendar.getInstance(); cal.set(1999, 12, 31); // Year, Month, Day System.out.print(cal.get(Calendar.YEAR) + " "); Date d = cal.getTime(); System.out.println(d.getDay()); } }
Solution 61: The Dating Game
This program creates a Calendar
instance that appears to represent New Year's Eve, 1999, and prints the year
followed by the day. It seems that the program should print 1999 31,
but it doesn't; it prints 2000 1. Could this be the dreaded Y2K
problem?
No, it's something much worse: It is the dreaded
Date/Calendar problem. When the Java platform was first
released, its only support for calendar calculations was the Date
class. This class was limited in power, especially when it came to support for
internationalization, and it had a basic design flaw: Date instances
were mutable. In release 1.1, the Calendar class was added to the
platform to rectify the shortcomings of Date; most Date
methods were deprecated. Unfortunately, this only made a bad situation worse.
Our program illustrates a few of the problems with the Date and
Calendar APIs.
The program's first bug is in the method invocation
cal.set(1999, 12, 31). When months are represented numerically,
convention dictates that the first month be assigned the number 1.
Unfortunately, Date represents January as 0,
and Calendar perpetuates this mistake. Therefore, this method
invocation sets the calendar to the thirty-first day of the thirteenth month of
1999. But the standard (Gregorian) calendar has only 12 months; surely this
method invocation should cause an IllegalArgumentException? It should,
but it doesn't. The Calendar class silently substitutes the first month
of the next year, in this case, 2000. This explains the first number printed by
our program (2000).
There are two ways to fix this problem. You could change the
second parameter of the cal.set invocation from 12 to
11, but that would be confusing. The number 11 would suggest November
to readers. It would be better to use the constant that Calendar
declares for this purpose, which is Calendar.DECEMBER.
What about the second number printed by the program? The
cal.set invocation clearly indicates that the calendar is set to the
thirty-first day of the month. The Date instance d represents
the same point in time as the Calendar, so its getday method
should return 31, but the program prints 1. What is going on?
To find out, you have to read the documentation, which says
that Date.getDay returns the day of the week represented by the Date instance, not the
day of the month. The returned value is 0-based, starting at Sunday, so
the 1 printed by the program indicates that January 31, 2000, fell on a
Monday. Note that the corresponding Calendar method,
get(Calendar.DAY_OF_WEEK), inexplicably returns a day-of-the-week value
that is 1-based, not 0-based like the value returned by its Date
counterpart.
There are also two ways to fix this
problem. You could call the confusingly named Date.date method, which
returns the day of the month. Like most Date methods, however, it is
deprecated, so you would be better off dispensing with Date entirely
and calling the Calendar method get(Calendar.DAY_OF_MONTH).
With both problems fixed, the program prints 1999 31 as expected:
public class DatingGame { public static void main(String[] args) { Calendar cal = Calendar.getInstance(); cal.set(1999, Calendar.DECEMBER, 31); System.out.print(cal.get(Calendar.YEAR) + " "); System.out.println(cal.get(Calendar.DAY_OF_MONTH)); } }
This puzzle only scratches the surface of the defects in
Calendar and Date. These APIs are minefields. Other serious
problems with Calendar include weak typing (nearly everything is an
int), an overly complex state-space, poor structure, inconsistent
naming, and inconsistent semantics. Be careful when
using Calendar or Date; always consult the API
documentation.
The lesson for API designers is: If you can't get it right the
first time, at least get it right the second; there may not be a third. If your
first attempt at an API has serious problems, your customers may be forgiving
and give you another chance. If your second attempt has problems, you may be
stuck with them for good.
No comments:
Post a Comment
Your comments are welcome!