Note the spelling. Java programmers rarely spell it correctly. This essay
looks a little different from my usual style. That is because was published in
2001 Volume 6 issue 6 of Java Developer’s Journal.
For newbies, dealing with dates and times are the probably the most confusing
aspect of Java. There are three reasons for this:
- The date and time classes are the most poorly designed of all the Sun class
libraries.
- The standard class libraries force you to deal with timezone and time of day,
even when they are irrelevant to your problem.
- The vocabulary used in the various date classes is inconsistent.
Vocabulary
- atomic
clock
- A highly accurate clock that monitors the frequency of light emited by
excited atoms or even a single atom, originally caesium atoms, or clock that is
accurately synched to such a clock.
- day of month
- The day of the month 1..31. Sometimes called the date.
- day of week
- Day of the week for a given date, e.g. Sunday = 1,… Saturday = 7.
- DST offset
- Daylight Saving Time offset. The number of milliseconds
correction to account for daylight saving time, 0 if daylight saving time is not
in effect for the timestamp specified. If a one-hour daylight saving is in
effect, the offset will be 3600000. You add the DST offset and the zone offset
to UTC to get local time.
- GMT
- A historic time standard derived by observing the sun at the prime meridian
at the Royal Observatory, Greenwich, England. It has been supplanted by the more
stable SI UTC standard based on an atomic clock.
- Gregorian
calendar
- Pope Gregory’s calendar we use today that has leap year corrections every 4,
100 and 400 years.
- ISO day of week
- Day of the week 1 to 7 for this date according to the ISO standard ISO-8601.
Monday = 1… Sunday = 7.
- ISO week number
- Week number 01 to 53 of the year this date falls in, according to the rules
of ISO standard ISO-8601. Week 1 of any year is the week that contains 4 January,
or equivalently week 1 of any year is the week that contains the first Thursday
in January. A week that lies partly in one year and partly in another is
assigned a number in the year in which most of its days lie. Most years have 52
weeks, but years that start on a Thursday and leap years that start on a
Wednesday have 53 weeks. January 1 may well be in week 53 of the previous year!
ISO specifies a lead 0 to create a 2-digit year. Java does not do that unless
you code the display specially.
- Julian Date
- I know of eight different definitions.
- leap
second
- an extra second added to the day every roughly every 3 to 7 years to keep
the UTC clock in sync with the rotation of the earth.
- leap year
- an extra day added to the year to keep the calendar in sync with the
revolution of the earth around the sun.
- month of year
- January to December. Note that in GregorianCalendar, January is month 0. In
contrast, in DateFormat, January is month 1.
- offset
- How many milliseconds difference local time is from UTC. If you live is
North America this will be a negative number. It is the sum of the zone offset
and the DST offset. You add the DST offset and the zone offset to UTC to get
local time.
- summer
time
- In much of the world, people adjust their clocks to trick themselves into
getting up an hour earlier in summer.
- timestamp
- An instant in cosmic time, expressed in milliseconds since January 1 1970 0:00
in UTC. It can be a positive or negative 64-bit long number. These are sometimes
called Dates and sometimes Times.
- time zone
- a region of the earth that keeps the same winter time. They may or may not
all flip to summer time at the same time and some parts may not flip at all.
- TimeZone
- A TimeZone is a Java class for a region of the
earth that keeps the same time. If they keep different time in summer and winter,
everyone is the region flips together. The TimeZone
class describes the offset from UTC in summer and winter time and when the flips
occur of a given time zone.
- UTC
- UTC stands for Coordinated Universal Time, huh? not CUT?
It was a weird compromise acronym half way between French and English. It is a
refinement of GMT. The SI (Systeme Internationale)
UTC standard based on a stable atomic clock. UTC is the time system used
internally in Java for Date, Calendar
and System.currentTimeMillis.
Windows NT/W2K/XP/W2K3/Vista
use UTC internally to track file dates.
- week of year
- There are many possible definitions. The default GregorianCalendar
definition depends on whether you consider Sunday or Monday as the first day of
the week setFirstDayOfWeek, (the default is locale
specific) and how many days you insist must be present in the first week of the
year setMinimalDaysInFirstWeek, (default 1). The first
week of the year is week 1. January 1 may sometimes be considered week 53 of the
previous year.
- zone offset
- milliseconds difference that local time is from UTC if you ignore any
daylight saving time correction. West of the prime meridian that runs through
Greenwich England, i.e. in west Africa and North and South America America, this
will be a negative number. Also Oceania (aka Oceana) in the Pacific Ocean east
of the Interational Date line will also have a negative offset. You add the DST
offset and the zone offset to UTC to get local time.
The Cast
You need to use quite a few different classes to solve even a simple problem
involving dates.
| Classes useful in Date calculation |
| class |
Purpose |
| com.mindprod.common11.BigDate |
A simpler date class for pure date calculations when you don’t want the
complication of TimeZones and times. Not part of Sun’s libraries. You can
download it from http://mindprod.com/products1.html#BIGDATE |
| java.text.DateFormat |
Used to convert a date to or from a String. Contains an associated TimeZone. |
| java.text.SimpleDateFormat |
Used to convert a date to or from a String when you want precise control
over the format. Contains an associated TimeZone. |
| java.util.Calendar |
Abstract class that is the mother of all Calendars such as GregorianCalendar.
It owns the dozens of magic date constants such as Calendar.JANUARY
= 0; Calendar.SUNDAY = 1; and Calendar.YEAR = 1. |
| java.util.Date |
Sun’s first attempt at a Date class. I refer to it as the lemon of Java. It
is now almost completely deprecated. It now just basically just a wrapper around
a UTC date/timestamp long milliseconds since 1970. Unfortunately, it is still
not completely gone. |
| java.util.GregorianCalendar |
Used to do date calculations. Each GregorianCalendar contains a UTC
timestamp, and a TimeZone. |
| java.util.TimeZone |
Contains the name of a timezone and how many hours difference from UTC that
timezone is. It also contains the rules for when daylight savings begins and
ends. Timezones are named after cities. They are not the usual names. |
Today’s Date
Here is how you discover today’s date and format it for display:
Displaying A Date
This program will convert a date to a String for display, using the default date
format. That format depends on what the user has configured as his preferred
date format in the OS.
DateFormat: Displaying A Date With Precise Control
If you want precise control of how your date looks, you can use a mask like this:
| DateFormat Formatting Characters |
| Letter |
Meaning |
| GG |
era: AD or BC |
| yy |
year, 2 digit |
| yyyyy |
year, 4 digit |
| MM |
Month in year, 01-12 |
| www |
ISO week in year, 01 to 53 |
| W |
Week in month, first week is 1 |
| DDD |
Day in year. Jan-01 is day 1 |
| dd |
Day in month, 1 to 31 |
| F |
ISO day of week, a number. Monday = 1… Sunday = 7 |
| EEE |
Day in week abbreviation, e.g. Tue |
| EEEE |
Day in week, e.g. Tuesday |
| aa |
AM/PM indicator |
| HH |
Hour in day (0-23), 24-hour time |
| kk |
Hour in day (1-24), 24-hour time |
| KK |
Hour in day for AM/PM (0-11) |
| hh |
Hour in day for AM/PM (1-12) |
| mm |
Minute in hour, 0-59 |
| ss |
Second in minute, 0-59 |
| SSS |
Millisecond, 000-999 |
| zzz |
Time zone abbreviation, e.g. PST |
| zzzzzzzz |
full Time zone, e.g. Pacific Daylight Time |
| Z |
Time zone offset e.g. -0700 |
Sun’s Javadoc on the
Complete List of SimpleDateFormat Mask Characters class : available:
Zulu Time
Here is how to display Zulu time, the way the US military displays UTC.
Local Time
Here is how to display local time, to the millisecond using the default locale TimeZone.
Parsing A Date
To convert a Date from a String to internal format is quite a production. Here
is one way of doing it:
Notes:
- Be very careful with TimeZones. If you don’t specify one, your date will be
interpreted using the the local default TimeZone. If the user has not configured
it correctly in his OS, you may get Pacific Standard time or GMT, without
warning.
Elapsed Time in Years, Months and Days
Elapsed Time In Hours Between Two Timestamps
Have a look at this example program to calculate how many hours until the next
presidential inauguration.
Notes:
- You specify the timestamp with GregorianCalendar.set
in terms of Eastern Standard time, not UTC. Internally the timestamp is stored
as UTC.
- You don’t create the TimeZone object with new.
- The getTime().getTime() is not an error. The first getTime
retrieves a Date object from the GregorianCalendar,
and the second retrieves a timestamp from the Date
object.
- Elapsed time in days is not simply hours / 24. It is much more complicated than
that.
How Long Until Christmas, Daddy?
This sounds like a simple problem. Programmers posted many different solutions
to the comp.lang.java.programmer
newsgroup before before the gurus stopped finding holes in the logic. Part of
the problem is that the question can have many different answers. I show eight
plausible solutions here:
Taking A Timestamp Apart
GregorianCalendar cal = new GregorianCalendar();
cal.setTimeInMillis( timestamp );
int year = cal.get( Calendar.YEAR );
int month = cal.get( Calendar.MONTH ) + 1;
int day = cal.get( Calendar.DAY_OF_MONTH );
int hour24 = cal.get( Calendar.HOUR_OF_DAY );
int hour12 = cal.get( Calendar.HOUR );
int amIs0OrPmIs1 = cal.get( Calendar.AM_PM );
int minute = cal.get( Calendar.MINUTE );
int second = cal.get( Calendar.SECOND );
Building A Timestamp from the Pieces
GregorianCalendar stamp = new GregorianCalendar();
stamp.clear();
stamp.set( year, month-1, day, hour, minute, second );
longtimestamp = stamp.getTimeInMillis();
Gotchas
java.util.GregorianCalendar has far fewer bugs and
gotchas than the old java.util.Date class but it is
still no picnic.
- Had there been programmers when Daylight Saving Time was first proposed, they
would have vetoed it as insane and intractable. With daylight saving, there is a
fundamental ambiguity. In the fall when you set your clocks back back one hour
at 2 AM there are two different instants in time both called 1:30 AM local time.
You can tell them apart only if you record whether you intended daylight saving
or standard time with the reading. Unfortunately, there is no way to tell GregorianCalendar
which you intended. You must resort to telling it the local time with the dummy
UTC TimeZone to avoid the ambiguity. Programmers usually close their eyes to
this problem and just hope nobody does anything during this hour.
- Millennium bug. The bugs are still not out of the Calendar classes. Even in JDK
1.3 there is a 2001 bug. Consider the following code:
GregorianCalendar gc = new GregorianCalendar();
gc.setLenient( false );
gc.set( 2001, 1, 1, 1, 0, 0 );
int year = gc.get ( Calendar.YEAR );
The bug disappears at 7AM on 2001/01/01 for MST.
- GregorianCalendar is controlled by a giant of pile of
untyped int magic constants. This technique totally destroys any hope of compile-time
error checking. For example to get the month you use GregorianCalendar.get(Calendar.MONTH));
- GregorianCalendar has the raw GregorianCalendar.get(Calendar.ZONE_OFFSET)
and the daylight savings GregorianCalendar.get(Calendar.DST_OFFSET),
but no way to get the actual timezone offset being used. You must get these two
separately and add them together.
- GregorianCalendar.set(year, month, day, hour, minute)
does not set the seconds to 0.
- DateFormat and GregorianCalendar
do not mesh properly. You must specify the Calendar
twice, once indirectly as a Date.
- If the user has not configured his timezone correctly it will default quietly to
either PST or GMT.
- In GregorianCalendar, Months are numbered starting at
January=0, rather than 1 as everyone else on the planet does. Yet days start at
1 as do days of the week with Sunday=1, Monday=2,… Saturday=7. Yet DateFormat.parse
behaves in the traditional way with January=1.
TimeZones
Here is a list of available timezones:
Standard : (DaylightSaving) : TimeZone
Here is a list of available TimeZones:
If the above TimeZones Java Applet does not work…
- This Java Applet needs Java 1.5 or later, version 1.5.0_15 or later recommended, ideally 1.6.0_06 and a recent browser.
- You should see an Applet above looking much like the screenshot. If you don’t, the following should help you get it working:
- If you are using Microsoft Internet Explorer, try another browser. Seriously. Microsoft has taken great pains, over and over, to screw up Java and every other mult-platform standardisation.
- If you are using Internet Explorer 7, you must allow blocked content permission for Active X to run. This also gives permission to Java to run. Click the Information bar, and then click Allow blocked content. Unfortunately, this also allows dangerous ActiveX code to run. However, you must do this in order to get access to perfectly-safe Java Applets running in a sandbox. This is part of Microsoft’s war on Java. Don’t put up with it! Use a different browser.
- To ensure your Java is up to date, check with Wassup.
- If the above Java Applet does not work, check the Java console for error messages.
- If the above Applet does not work, you might have better luck with the downloadable version.
- If you still can’t get the program working click HELP for more detail.
- If you can’t get the program working after trying the advice above and from the HELP button below, have bugs to report or ideas to improve the program or its documentation, please send me an email at
.
Get New Java Get New Browser
Help
- Add column 1 in hours to UTC to get local standard time.
- Add column 2 in hours to UTC to get local daylight saving time.
- Use UTC when you want no time zone at all.
- Use Asia/Riyadh for Arabia
Standard Time. Asia/Riyadh87, Asia/Riyadh88
and Asia/Riyadh89 are 3 hours and 7 minutes east
of UTC. This is the offset used in 1987 to 1989. Actually it was 3 hours 7
minutes and 4 seconds to approximate solar time. Prior to 1950 they used 3:06:52.
In the period 1951-1986 and 1990 onward they used a simple 3 hour difference. In
Islamic tradition, the day starts at sunset.
- Note how much many aliases there are for the same timezone. I speculate there
are two reasons for this:
- To anticipate some geographical region adopting a quirky daylight saving rule in
future. There would be no need adjust to a new split timezone to adopt the new
rule. Only the tables built into Java would need to be adjusted.
- It makes it easier for people to find their own timezone. They need find only a
nearby city without having to consult a map to find a distant city on the same
longitude. Often they can find their own city directly.
Learning More
The calendar classes are full of surprises. To learn more about them see date
gotchas.
Dealing with pure dates is much simpler using the BigDate
class. You can download it from http://mindprod.com/products1.html#BIGDATE
Sun is going to try redesigning Date/Time a third time, in Java 1.7, this time
based on Joda Time,
so you can also have dates without times (like BigDate),
times without dates, intervals…
You need to use quite a few different classes to solve even a simple problem
involving dates. Examine the
Sun’s Javadoc on the
Calendar class : available:
Sun’s Javadoc on the
Date class : available:
Sun’s Javadoc on the
DateFormat class : available:
Sun’s Javadoc on the
GregorianCalendar class : available:
Sun’s Javadoc on the
SimpleDateFormat class : available:
Sun’s Javadoc on the
Complete List of SimpleDateFormat Mask Characters class : available:
Sun’s Javadoc on the
TimeZone class : available:
.