Credit to Paul Hill who completely rewrote this section, and to the programmers at IBM who implemented much of the Java Date code, and reviewed this section of the FAQ for accuracy.
See http://www.sun.com/y2000/jumppage-temp/jdk.html
JDK 1.1.8_09a is Y2K compliant, as well as 1.1.7_08a. The two Java2 SDKs that have been certified are 1.2.1_03a & 1.2.1_04.
The Date class, as you can see from the discussion, contains more than enough resolution to represent dates in this century and the next and the last. The SimpleDateFormat when parsing a 2 digit year could cause problems; see discussion below.
In JDK 1.1 the java.util.Date class was split to provide better support for timezones, and internationalization.
The classes specifically related to dates are summarized below:
- The class Date represents a specific instant in time, with millisecond precision.
- The class TimeZone is an abstract class that represents a time zone offset, and also figures out daylight savings time adjustment.
- The class SimpleTimeZone is the only concrete subclass of TimeZone in the JDK. It is what defines an ordinary timezone with a simple daylight savings and daylight savings time period.
- The class Calendar is an abstract class for converting between a Date object and a set of integer fields such as year, month, day, and hour.
- The class GregorianCalendar is the only concrete subclass of Calendar in the JDK. It does the Date-to-fields conversions for the calendar system in common use.
- The class DateFormat is an abstract class that lets you convert a Date to a printable string with fields in the way you want (e.g. dd/mm/yy or dd.MMM.yyyy).
- The class SimpleDateFormat is the only concrete subclass of DateFormat in the JDK. It takes a format string and either parses a string to produce a date or takes a date and produces a string.
At least one critic has used the term "baroque" when describing the complexities of the Java date related classes, but other critics would spell that "broke". The good news is that as of JDK 1.2 all of the common problems have been corrected and many of the bugs were corrected in 1.1.4 and 1.1.6. Even in 1.1.1, you can avoid most of the most annoying bugs by always keeping in mind which timezone each class is using.
A java.util.Date stores a moment in time as a long integer representing the number of milliseconds since 00:00:00 Jan 1, 1970 UTC (Coordinated Universal Time). This zero moment is known as the "Epoch". This is the same Epoch as is used on Unix systems. Dates earlier than the Epoch are represented as negative numbers, counting away from 1/1/1970.
The scheme is sufficient to represent dates from 292,269,053 B.C. to 292,272,993 A.D. (64 bits covers -9,223,372,036,854,775,808 to +9,223,372,036,854,775,807 milliseconds. But note that prior to JDK 1.2, a GregorianCalendar will not accept values earlier than 4713 B.C.
A java.util.Date is the light-weight object intended to just hold a
millisecond value. It is used to hold, communicate and
store a moment in time. Other tasks like creating a formated
string, or calculating dates are best done using other classes.
No, but it is close enough for
most human time-keeping purposes. On most computers, it
only represents the time since the epoch as
converted from the value on
the underlying hardware.
If you have hardware that is synchronized
with an atomic clock your time is UTC; most hardware assumes a day is 24
hours long, but there have been more than 20 leap
seconds added to UTC, since the first one was added in 1972.
The default value of a date object is the current time, so the following code creates a date object that contains the current time.
Date now = new Date();
No. Instead of creating a Calendar
and pulling out all of the appropriate fields and making a string, you
could use SimpleDateFormat.format()
to create a string.
Mostly because the original java.util.Date
was not completely aware of the timezone and "not amenable to
internationalization".
To make it timezone aware and internationalizable would have required adding
some of the functionality which can now be seen in java.util.Calendar
and some of the functionality in java.util.DateFormat.
If you find the combination all of the date related classes complex, just
be glad they were separated into different classes.
You could consider using the BigDate class
written by Roedy Green, and available in his very informative
glossary (search for BigDate).
If you want to store the result in a database as a Date or TimeStamp, you should
read
the section below on java.sql.Date.
The best choice is to use SimpleDateFormat.parse() to create a java.util.Date object.
The Date constructor that accepts a string calls Date.parse( String
). The Date.parse() function had its own rules for converting 2 digit years
(it used a 1980 pivot date) and other limitiations which makes it of limited
value. Other "features" of Date.parse() that are not supported in
SimpleDate have not been missed by many developers.
The constructor
GregorianCalendar(int
year, int month, int date) is the newer replacement. Other choices are the
Calendar.set( year, month, day ) method. Note that the year
in the GregorianCalendar starts at 1 A.D., not at 1901 like the old Date
constructor.
The following codes displays the ID of the current default timezone.
System.out.println( TimeZone.getDefault().getID() );
The value of the default timezone is based
on the value of the system property "user.timezone". The
JVM is supposed to set this value. In releases such as JDK
1.1 the value of user.timezone was often not set to anything,
so TimeZone.getDefault() used its own built in "fallback" value (the default
when there is no default value). In later JDK 1.1 releases and in
JDK 1.2 the setting of the value of user.timezone is much better
and the "fallback" value is now GMT (Greenwich Mean Time). Up until JDK
1.1.3, the fallback value was "PST" (North American Pacific Timezone).
Not until JDK 1.2. In JDK 1.1, Date.toString() and Calendar used the value of TimeZone.getDefault() which could often be undefined (see the previous question). In JDK 1.1, The Calendar in a SimpleDateFormat was set to the 1st timezone resource for the locale (for the US this is PST).
System.out.println( "TZ default = " + TimeZone.getDefault().getID() ); sdf = DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG ); System.out.println( "Date format TZ = " + sdf.getTimeZone().getID() ); Calendar cal = Calendar.getInstance(); System.out.println( "Calendar TZ = " + cal.getTimeZone().getID() );When run on a system running JDK 1.1.6, NOT in the North American Pacific Time nor in the GMT timezone results in:
Timezone default = GMT Date format TZ = PST Calendar TZ = GMTThis example shows two bugs, the value of user.timezone is undefined, so its defaulting to GMT (see discussion of TimeZone.getDefault()) and it shows that the DateFormat depends on the 1st locale entry which in this case is PST.
If you don't want the DateFormat to use the Locale timezone, see the code provided below.
No. The ID that you use to select a timezone with TimeZone.getTimeZone refers to a predefined timezone that contains daylight savings information (when applicable). For example, the following code selects the timezone used in New York, USA.
// Get the North American Eastern Time definition. TimeZone theTz = TimeZone.getTimeZone( "EST" ); // get a really detailed date format and set it to the right timezone DateFormat df = DateFormat.getDateTimeInstance( DateFormat.LONG, DateFormat.LONG ); df.setTimeZone( theTz ); // create a date in the locale's calendar, set its timezone and hour. Calendar day = Calendar.getInstance(); day.setTimeZone( theTz ); day.set( 1998, Calendar.FEBRUARY, 1 ); day.set( Calendar.HOUR, 12 ); // print that date/time and // that date/time 150 full days of milliseconds later. System.out.println( df.format( day.getTime() ) ); System.out.println( df.format( new Date( day.getTime().getTime() + // get the millis 150L*24*60*60*1000L ) ) ); // add exactly 150 days of millisResults in:
February 1, 1998 12:00:00 PM EST July 1, 1998 1:00:00 PM EDTNotice that this example selected something referred to as "EST", but that this TimeZone was aware of the daylight savings time change and either printed as "EST" or "EDT". The confusion is reduced in JDK 1.2: you can use longer TimeZone IDs, and each maps to its own set of text resources. For example the following IDs are 5 hours West of GMT and have various DST rules: "America/Nassau", "America/Montreal", "America/Havana", "America/Port-au-Prince", "America/Grand_Turk", "America/New_York" and "EST".
You can look at a list of other timezone names and offsets in the file $JAVAHOME/src/java/util/TimeZone.java
You can create a TimeZone object with the GMT offset of your choice. The following code creates British Time, a timezone that was not defined in 1.1.
britTime = new SimpleTimeZone(0*ONE_HOUR, "Europe/London" /*GMT/BST*/, Calendar.MARCH, -1, Calendar.SUNDAY /*DOW_IN_DOM*/, 1*ONE_HOUR, Calendar.OCTOBER,-1, Calendar.SUNDAY /*DOW_IN_DOM*/, 1*ONE_HOUR, 1*ONE_HOUR), TimeZone.setDefault( britTime );Or you can then apply that TimeZone to a particular Calendar object like so:
Calendar myCal = Calendar.getInstance(); myCal.setTimeZone( britTime );
If you are running 1.2, you can choose this existing timezone as the default with the code:
TimeZone.setDefault( TimeZone.getTimeZone( "Europe/London" ) );Note that BST is defined from JDK 1.1 and later, but it is Bangladesh Standard Time. For a longer example of creating and testing the British timezone, Tony Dahlman provides a nice example in his BSumTime.java code.
You can create an arbitrary TimeZone object with the code below. In most or all other timezones, daylight savings time is handled automatically and internally. But GMT is the reference for all other timezones, and so does not have the summertime update applied automatically. The rules for BST can be found at http://www.rog.nmm.ac.uk/leaflets/summer/summer.html
Here is the code to create a British Summer Time timezone that is offset one hour from GMT between two dates:
import java.util.*; import java.text.*; // create a BST timezone (code courtesy of Tony Dahlman). public static GregorianCalendar setBritSummTime(String zoneName){ // Set up the default GMT0BST time zone SimpleTimeZone bst_tz = new SimpleTimeZone( 0, // no offset from GMT zoneName, // individualized tz id // last Sun Mar 1AM Calendar.MARCH,-1,Calendar.SUNDAY,1*60*60*1000, // last Sun Oct 2AM Calendar.OCTOBER,-1,Calendar.SUNDAY,2*60*60*1000 ); SimpleTimeZone.setDefault(bst_tz); // Apply TimeZone to create a Calendar object for UK locale return (new GregorianCalendar(bst_tz,Locale.UK) ); }and here is how you would print out values using BST:
// create a template for printing the date DateFormat df = DateFormat.getTimeInstance( DateFormat.LONG,Locale.UK); // tell the template to use BST tz. df.setTimeZone(TimeZone.getDefault()); System.out.println("Using British Summer Time " +"the time is: " + df.format( BritishSummerTime.getTime() ) ); // Now get and compare with current time in GMT df.setTimeZone(TimeZone.getTimeZone("GMT") ); System.out.println("\nCurrent time in GMT is: " + df.format(BritishSummerTime.getTime() ) );In the winter, this BST zone is aligned with GMT; in the summer it is one hour later (4 a.m. GMT is 5 a.m. BST).
You can look at a list of timezone names and offsets in the file $JAVAHOME/src/java/util/TimeZone.java
If you have a Date use:
myCal.setTime( myDate );If you have a set of integers representing the year, month and day of month use:
Calendar myCal = Calendar.getInstance(); myCal.set( 1998, Calendar.MARCH, 15 );Note: Months start with January = 0!
The following code shows how to get some fields from a Date.
Calendar g = Calendar.getInstance(); g.setTime( aDate ); int year = g.get( Calendar.YEAR ); int mon = g.get( Calendar.MONTH ); int date = g.get( Calendar.DATE ); mon++; // in class Calendar & GregCal, months run 0-11 ;-( System.out.println( mon + "/" + date + "/" + year);If you want to build a string that has a formatted date consider using SimpleDateFormat.format().
Either way is correct, it depends on what you want to be able to do. You should use Calendar.getInstance(), if you want your code to be ready when the loading of other Calendars is added to the JDK and some other calendar is the default for the locale. A particular locale might have configured a Hebrew or Islamic Calendar as the default calendar and you might want a user to enter a date in his own Calendar, i.e. 1-Jan-2000 (Gregorian) = 23-Tevet-576 (Hebrew) = 24-Ramadan-1420 (Islamic). If you really are trying to place a particular Gregorian date, i.e. 4-July-1776, into a Date object, you might as well create a GregorianCalendar directly.
In JDK 1.1.0 the Calendar class did not update all of its fields until you called getTime to retrieve the Date that corresponds to the fields in the Calendar. To get the earlier version of the Calendar to "turn the crank" and calculate all fields you can use the trick:
myCal.setTime( myCal.getTime() ) // pull the date out and put it back in.
You need to be aware that months start with January equal to 0. A better way to create that date would be:
independanceDayUSA = new GregorianCalendar( 1776, Calendar.JULY, 4 );
The short answer is: these values are not constants. While some date calculations would find these useful, it is important to remember that in areas with daylight savings time rules, there are two days per year that are not 24 hours long, therefore not all weeks are the same length (2 out of 52). Also, because of leap years, not all years are the same length.
If you are adding values to a calendar consider using either add or roll; for example:
myCal.add(Calendar.YEAR, 1 ); // get a value 1 year later.
The GregorianCalendar class uses the value set by setMinimalDaysInFirstWeek() to determine if the fractional week at the beginning of the year should be week 1 or week 0. If you don't change it, any fractional week could be week 1, depending on the value defined for the locale.
The Calendar uses the TimeZone.getDefault()
(see discussion under TimeZone).
Probably not. The Calendar class
is a much larger than a Date object. Many other interfaces in standard
APIs are defined using a Date object. Use Date objects to hold, store or
communicate a date-time value. Use a Calendar object to manipulate
a date-time value.
January 1, 4713 B.C. is the "epoch" date
for the Julian Day calendar which was invented in the 16th century by
Joseph Justus Scaliger. "[T]he Julian day calendar, ... does not use individual
years at all, but a cycle of 7980 astronomical years that counts a day
at a time, with no fractional days, no mean year, and no leap years.
He came up with his number by multiplying three chronological cycles: an
18-year solar cycle, a 19-year lunar cycle, and the 15-year indication
period used by Romans. All three cycles began together at the same
moment at the start of the "Julian cycle. ... [This] Calendar lives on
among astronomers."
-- David Ewing Duncan, "Calendar", Avon Books, 1998; p 207
Note that the Julian Day calendar is not the same as the Julian calendar. The Julian Calendar is named for Julius Caesar. The Julian Calendar was used in the Europe from what we now call January 1, 45 B.C. until at least October 4, 1582 and is still used today by the Eastern Orthodox Church to date holidays.
The limitation on dates prior to 4713 BC has been dropped in JDK 1.2.
The date of change from the Julian to the Gregorian calendar depends on where you lived at the time. The date can vary from 1582 (most Catholic countries, which of course followed the Papal edict) to 1949 (China). The date of the cutover from using the Julian Calendar (leap years every 4 years) to using the Gregorian Calendar (every 4 years, except every 100 unless divisable by 400) is controlled by the method GregorianCalendar.setGregorianChange(Date).
It is also the case that January 1 was not always the beginning of the year. January 1 was standardized by Julius Caesar in 45 B.C. and Pope Gregory XIII in 1582, but others who used the Julian Calendar between those dates used other dates for New Years Day. (Anyone who has ever been involved in a standardization effort may find it interesting that neither an emperor nor a pope could actually complete the standardization effort).
The Calendar class uses a TimeZone which does not handle historical changes, i.e. the SimpleTimeZone contains only three dates: the "spring forward" and "fall back" dates, and a date that the DST starts (see SimpleTimeZone.setStartYear() ). If the local definitions have changed then a date/time may not accurately reflect the historical local time.
As noted above, the Date object does not usually include leap seconds, unless your hardware includes leap seconds.
While the Calendar class is more than useful for international business, it may not be what you want for doing UTC timebased calculations or historical dates and times without a more careful analysis of its design limits.
The following code illustrates the technique:
import java.text.*; public class DateTest { public static void main( String[] args ) { SimpleDateFormat df1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.S"); SimpleDateFormat df2 = new SimpleDateFormat("dd-MMM-yy"); String startdatetime = "1998-09-09 06:51:27.0"; try { System.out.println("Date is " + df2.format( df1.parse(startdatetime) )); } catch (ParseException pe) { System.out.println("ParseException " + pe ); } } }When run, the program outputs "Date is 09-Sep-98"
The easiest way to parse a date that is in a known format is to use SimpleDateFormat.parse().
DateFormat df = new SimpleDateFormat( "HH:mm" ); df.setTimeZone( TimeZone.getDefault() ); // if using JDK 1.1 libraries. df.setLenient( false ); // to not allow 26:65 etc. Date lateLunchOnDayZero = df.parse( "12:30" ); System.out.println( lateLunchOnDayZero );The above code would result in (when in the MST timezone):
Thu Jan 01 12:30:00 MST 1970To parse other date and time fields, refer to the SimpleDateFormat documentation.
The easiest way to create a string from a date is to use a SimpleDateFormat.format(). The following code illustrates how this can be done.
DateFormat df = new SimpleDateFormat( "yyyy.MMM.dd HH:mm:ss.SSS z" ); df.setTimeZone( TimeZone.getDefault() ); // JDK 1.1 System.out.println( df.format( d ) ); // where d is a DateFor other possible fields from the calendar, see the document for SimpleDateFormat.
In JDK 1.1, the SimpleDateFormat uses the first timezone defined for the locale. In JDK 1.2, it uses the default timezone. See the discussion above on how this differs from the Calendar class).
The following code sets the timezone of a DateFormat to the current default.
DateFormat df = DateFormat.getDateInstance(); df.setTimeZone(TimeZone.getDefault());or to set it to a timezone of your choice:
df.setTimeZone(TimeZone.getTimeZone( "MST" ) ) // Mtn Time, Denver USA
In JDK 1.1, the default start for the century
used by SimpleDateFormat for 2 digit years is 80 years before the current
date.
This means that in 1998: 1 = 2001, 2 = 2002, ... 17 = 2017, 18 =
2018,
19 = 1919, 20 = 1920, ... 98 = 1998, 99 = 1999,
In JDK 1.2 you can change this "default century start date" with the
method set2DigitYearStart( Date) and get its current value
with the method get2DigitYearStart(). One thing to note is that
since set2DigitYearStart takes a date not a year, you can have your default
century begin at any day or hour.
When running under JDK 1.1, it is probably best to avoid two-digit
year
fields, when the dates entered could possibly fall outside of the range
-- now less 80 years and now plus 20 years. If you want to allow
two-digit year fields in JDK 1.2 and beyond, consider setting the
2DigitYearStart
property to something appropriate, For example, set it to today,
when all dates to be entered are in the future (i.e. an expiration date),
or set it to today less 100 years, when the value is always in the past
(i.e. birthdate, death date).
No. It means that any code you write that (1) allows the entry of 2 digit years and (2) does not make sure they are in an appropriate century, would not pass a careful Y2K analysis. This code was put here so you could sensibly read old files with non-Y2K compliant dates, not so you could create new ones. Once you are using JDK 1.2 it is better to set the 2DigitYearStart property to something appropriate for any two-digit year field which you are parsing.
This is another hidden use of the
default java.util.TimeZone. If you have
carefully set every timezone in every Calendar
and DateFormat you are using, but you don't set the default in
java.util.TimeZone when a java.util.Date is converted to a java.sql.Date
you may not end up with the value you expected in your database.
If you print the java.sql.Timestamp directly you will see this problem. The following code demonstrates this surprising behavior.
// incorrect use of java.sql.Timestamp DateFormat df = new SimpleDateFormat( "MM/dd/yy hh:mm:ss.SSS a" ); df.setTimeZone( TimeZone.getDefault() ); // needed in JDK 1.1 java.sql.Timestamp t = new java.sql.Timestamp( 94, Calendar.JANUARY, 1, 13, 45, 59, 987654321 ); System.out.println( df.format( t ) ) ; // Wrong! no fractions of a second.The results of the above code are:
01/01/94 01:45:59.000 PMThe above code is using whatever is in the super class (java.util.Date) and assumes all of those parts are filled in. java.sql.Timestamp could have stored the whole milliseconds in the millisecond part of a java.util.Date, and stored the nanoseconds that are not whole milliseconds in an additional field. They chose to ignore the fractions of a second in the java.util.Date and put all fractional parts in an additional nanosecond field.
The following code shows how to convert a java.sql.timestamp to a java.util.Date.
Date d = new Date(t.getTime() + (t.getNanos() / 1000000 )); // 1 Milli = 1x10^6 Nanos System.out.println( df.format( d ) ) ; // Right! At least we have the millisThe result of the above code is a better approximation of the timestamp value:
01/01/94 01:45:59.987 PM
See separate Article.