| ★ wanayoo — archive 1999 http://developer.netscape.com/viewsource/goodman_dateobject.html | Nouvelle recherche | Portail wanayoo |
Readers who belonged to the Macintosh community about ten years ago may remember my personal information management (PIM) commercial product, called Focal Point. One of the modules of this PIM was the ever-popular appointment calendar. Perhaps the most valuable lessons I learned from creating and upgrading that application were the intricacies of programming dates and time, both the internal algorithms and user interfaces designed for a worldwide audience.
This experience makes me, I suppose, hypersensitive to the implementation of date and time in a programming environment, such as JavaScript. Not that I have any intention of implementing an appointment calendar in JavaScript. (Except as a client-side assistant to a substantial server-based program, the language is not suited to this kind of application.) But given the powers of the Date object in JavaScript in Navigator 3.0, I'm surprised by the lack of date- and time-oriented scripting in Web pages.
The purpose of this article is to explain the concepts behind JavaScript's Date object and describe a few practical implications of this object in both plain and forms-based HTML pages. Along the way, I'll also point out some of the pitfalls and bugs you'll need to work around, including a few recent discoveries.
Note: There were many global and platform-specific structural changes to the Date object between Navigator versions 2 and 3. This article discusses the Date object only as implemented in Navigator 3. Communicator will likely follow the Navigator 3 mold as well as deal with the gremlins described later in this article.
WHERE THE DATE OBJECT COMES FROM
Many scripters have seen the JavaScript object roadmap from my book (available in Adobe Acrobat format as a free FTP download). In this roadmap, the Date object stands apart from the window-document object hierarchy, because a Date is a computational object, rather than one that reflects "physical" elements in an HTML page. The object has no fewer than 22 methods that give the scripter access to every time and date component of the object.
The idea for the Date object and its methods didn't fall out of the sky as a feature unique to JavaScript. No, the syntax is borrowed directly from Java 1.0. In fact, once you master the JavaScript Date object, you will have also mastered 95 percent of Java's Date object. (Note: Java 1.1 has enhanced date handling powers with a new object called Calendar.)
CREATING A BASIC DATE OBJECT
Its Java heritage explains why using the Date object requires what in Java is a very common occurrence: creating an instance of--instantiating--the object before any statement can examine, change, or perform operations on a date. For example, to create a default Date object (whose date is today's date as indicated by the client system's internal clock), the syntax is the following:
var today = new Date()
When no parameters accompany the Date() object constructor, JavaScript automatically reads the client's internal clock setting, and creates the object with the date and time at the instant at which the object is created. Therefore, after executing the above statement, the arbitrarily-named variable today is a reference to a Date object in memory that holds the current date and time. In other words, the object takes a snapshot of the client's system clock; the content of this date/time "photo" doesn't change unless a script statement invokes one of the Date object's methods that modifies the object.
There are several more interesting ways to create a Date object -- all of which entail assigning a specific date and time to the object other than the current moment. For the sake of completeness the table "Date Object Constructors," shows all Date object constructor formats for assigning specific dates.
Date Object Constructors
| Syntax | Example |
|---|---|
| new Date("Month dd, yyyy hh:mm:ss") | new Date("September 11, 1997 08:30:00") |
| new Date("Month dd, yyyy") | new Date("September 11, 1997") |
| new Date(yy,mm,dd,hh,mm,ss) | new Date(97,8,11,8,30,00) |
| new Date(yy,mm,dd) | new Date(97,8,11) |
| new Date(GMTmilliseconds) | new Date(873991800000) |
To fully appreciate how these Date object constructors work, let's put the Date object under a microscope to see what it's doing internally.
INSIDE THE DATE OBJECT
When you create a Date object with the current time and date, there is a lot more going on inside JavaScript than you may be aware of. If you have trouble figuring out what time it is in a neighboring time zone, then the Date object's innards are going to make your head hurt.
To truly understand all of this, you need a working knowledge of the recognized world time reference point, which runs through Greenwich, England. Two common names for this time zone are Greenwich Mean Time (GMT) and Coordinated Universal Time (UTC). Although there are tiny differences between the two (the difference between atomic clocks and astronomical observations), for our purposes, we can regard them as equals. If you are interested in how these terms came about, excellent on-line sources include The Time Service Department of the U.S. Naval Observatory and Greenwich 2000.
The basic point to understand about GMT and time zones around the world is that if you know the time at GMT, you can determine the local time anywhere on the planet, because each time zone is measured relative to GMT, by international convention. When the sun is directly overhead the GMT zone, for example, all time zones to the east (through Russia, Asia, Australia, and part way across the Pacific Ocean until you reach the International Date Line) are later in the day than noon; the noon-day sun has already been there, done that. Conversely, all time zones to the west (across the Atlantic, through North and South America, and onward across the Pacific up to the date line) are earlier in the day, and have lunchtime to look forward to. The figure "World Time Relative to Noon GMT" represents a snapshot of the world's time at exactly noon GMT.
World Time Relative to Noon GMT
![]() |
To eliminate the vaguaries of the world's time zones (some of which shift during part of the year for the equivalent of America's Daylight Saving Time), the JavaScript (and Java) Date object stores its information as GMT time. More precisely, the date value is stored as the number of milliseconds before (positive) or after (negative) zero hours GMT on January 1, 1970.
DATE OBJECT VALUES
If you have played at all with the Date object, you may be quite confused about why GMT has to come into the picture at all. After all, if you sit in front of a Windows-based PC at Netscape's world headquarters in Mountain View, California, and get the value of the current date and time, you see a value that looks like this:
Thu Mar 20 23:09:48 Pacific Standard Time 1997
On a Mac OS computer in the same location, the value looks like this:
Thu Mar 20 23:09:48 1997
Take a look at the example in "Try it on Your Client" to see how it looks in your system. What you see there, however, is the Date object being automatically converted to your client's time zone--even though it's the GMT date and time that's stored in the Date object. Also, when you use the Date object methods that get and set components of a Date object (year, month, date, day of the week, hour, minute, and second), those values are reflected in your client's local time, not GMT. In fact, the only values that reveal the Date object's GMT date are the millisecond measurement (accessible via the getTime() method) and the GMT string conversion (via the toGMTString() method).
See how the Date object reveals its value to your client software:
Here's a string representation of the Date object:
All of these conversions to local time, of course, rely on the proper setting of the client computer's clock and relevant Control Panel settings about the time zone of the computer's physical location. In a sense, this is a wild-card issue; as a scripter, you can never be sure that the client's clock and time zone are set correctly.
DATE OBJECT METHODS AND VALUES
Given the potential confusion between GMT and local date and time values, I think it's a good idea to look at some specific values of a Date object's components and see precisely which values are GMT-based and which are local. The table "Date Object Methods" shows all Date object methods and examples of their values. (For more details on the syntax, see the Date object discussion in the "Navigator Handbook JavaScript Guide.") In the Method column of the table, dateObj is a placeholder for any variable that references a previously created Date object. You can experiment with a number of dates and time selections while viewing the results in the table.
OBTAINING AN ACCURATE DATE
Because your scripts are at the mercy of an accurate client clock setting, I don't advise relying exclusively on the client to supply the precise GMT time value if your application needs that data for a form's date or time field. For example, if it requires the exact time of a form submission, you might want to use a form submission CGI do the timestamping with the server's clock (assuming you have control over that machine's clock settings).
If your scripts use dates for calculations or comparisons, consider carefully how you generate Date objects. Bear in mind that visitors are most likely to be located in time zones other than yours. Therefore, if you attempt to create a Date object for a specific date and time, the GMT Date object value for visitors from other time zones will be different than the value you used to test out your page. Consider the following Date object constructor statement:
var myDate = new Date(1997,11,24,18,0,0)
Those constructor values represent 6:00 P.M. on December 24, 1997. For someone in the Eastern Standard Time zone, the object's GMT value of myDate is 11:00 P.M. on December 24; for someone in the Pacific Standard Time zone, the object's GMT value is 2:00 A.M. on December 25.
To establish a firm GMT date and time in an object, you need to use one of the last two methods of "Date Object Methods" (Date.parse() and Date.UTC()) to first obtain the GMT millisecond value. You can then use that millisecond value as a parameter to the Date() constructor.
Each of these methods requires a specific parameter to do its job. Date.UTC() may be the simpler of the two to use, provided you know the GMT time you wish to create. The parameters consist of a comma-delimited list of integer values corresponding to the year, month, date, hour, minute, and second, in that order. That's what I used in the example shown just above.
If you aren't comfortable with converting your local time to GMT just yet, you can use the Date.parse() method instead. Its parameter is a string patterned after a date format established by the Internet Engineering Task Force. What I like about this format is that you can specify either the GMT time or the local time along with an offset from GMT. For example, let's say you are in New York (Eastern Standard Time) and want to create a Date object that corresponds to 6:00 P.M. EST on Christmas Eve. Any of the following statements will do the trick:
var myDate = new Date("24 Dec 1997 23:00:00 GMT")
var myDate = new Date("24 Dec 1997 18:00:00 GMT+0500")
var myDate = new Date("24 Dec 1997 18:00:00 EST")
In the second version, the GMT+0500 indicates that the time zone is five hours west of GMT. JavaScript (as influenced by Java) also knows the most common time zone abbreviations used in the western hemisphere (EST, EDT, CST, CDT, MST, MDT, PST, and PDT). That no other time zones are allowed is a sign that the Java model suffers from an American myopia when it comes to global dates and times. Even so, the application of the GMT format, plus or minus an amount of time represented by the hhmm value after GMT, means that this system can be used around the Globe.
DOING THE MATH
When it comes to scripting date calculations, such as the number of days between dates or the date six weeks from today, you can use the milliseconds value of Date objects. I also find it convenient to define some global variables in documents that perform date arithmetic, as shown in the following:
var MINUTE = 60 * 1000
var HOUR = MINUTE * 60
var DAY = HOUR * 24
var WEEK = DAY * 7
I use all uppercase names because I treat these values like constants -- a common stylistic convention. With these variables predefined, I can use them as shortcuts in calculations. For example, to calculate the number of days between dates, take the difference between two Date object values and then divide that by the DAY variable, as shown here:
var today = new Date()
var xmas = new Date(1997,11,25)
var shoppingDays = (xmas - today) / DAY
Notice that you can subtract two Date objects directly to determine their difference in milliseconds. But for other calculations involving Date objects and other value types, you must first convert the objects to milliseconds via the getTime() method. For example, say you want to create a new object, futureDate, with the date and time for precisely six weeks from now. First, you'd capture the current date as (GMT) milliseconds; next, you'd add that value to six times the product of the WEEK global variable, as shown in the following code:
var today = (new Date()).getTime()
var futureDate = new Date(today + (WEEK * 6))
The most difficult part of working with the Date object is dealing with some platform-specific bugs (primarily on the Mac OS platform) and one annoying inconsistency.
The Mac OS version of Navigator 3 exhibits a couple of problems you should be aware of. One occurs only when the Date & Time control panel has Daylight Saving time turned on. When Daylight Saving is engaged, JavaScript miscalculates the conversion between local and GMT date values by one hour (in Navigator 2, this discrepancy was an entire day). For example, consider the following Date object constructor and the result:
new Date("July 4, 1997 12:00:00")
// result on Daylight Saving Mac --> Fri Jul 4 13:00:00 1997
To handle this problem, I define one more global variable in any page that includes date calculations. This variable, DATEADJUSTMENT, is calculated for every platform, just in case there's an unknown bug lurking on a platform I can't test. Here are the statements that set the variable:
function adjustDate() {
var base = new Date()
var testDate = base
testDate = testDate.toLocaleString()
testDate = new Date(testDate)
DATEADJUSTMENT = testDate.getTime() - base.getTime()
}
Each time a new date is created, the DATEADJUSTMENT value must be subtracted from it. The process of creating an accurate Date object this way requires that you create two Date objects; the adjustment must be factored for both constructors, as shown in the following:
var nowInMS = (new Date().getTime() - (2 * DATEADJUSTMENT)
var nowDateObject = new Date(nowInMS)
When this date adjustment bug doesn't afflict the client, the variable does contain a small number of milliseconds (the time it takes to execute the adjustDate() function). But unless you're calculating times at that granularity, the tiny adjustment won't affect normal date and time calculations.
MAC OS TIME ZONES
Another, more serious bug affects Mac OS users whose time zone settings place them at GMT or eastward to the International Date Line. The problem is that JavaScript miscalculates the time zone offset entirely, causing Date objects created from the internal clock or via constructors that use local values (that is, all but the constructor for GMT milliseconds) to be one full day later than the intended date.
At the heart of the matter is that, on the Mac OS, JavaScript counts the time zone offset westward from GMT all the way around the Globe instead of using negative numbers east from the GMT to the International Date Line. The time zone offset for Sydney, Australia, should be -600; on Mac OS computers, however, JavaScript renders it as a positive 840. The erroneous counting continues all the way to GMT, whose offset value is rendered as 1440 (60 minutes times 24 zones) instead of zero.
The strange behavior here is that the Mac OS reflects the proper local time and date components but gets the Date object GMT value wrong by one full day. Therefore, if your script requires an accurate GMT version of the local date, the script needs to factor this zone error whenever the GMT value is involved.
To adjust for this potential error, I add one more global variable definition to the adjustDate() function, as follows:
function adjustDate() {
var base = new Date()
var testDate = base
testDate = testDate.toLocaleString()
testDate = new Date(testDate)
DATEADJUSTMENT = testDate.getTime() - base.getTime() - zoneError
ZONEERROR = (base.getTimezoneOffset() >= 720) ? DAY : 0
}
To create a Date object with the corrected GMT value, here is how to use both date adjustment global variables:
var nowInMS = (new Date().getTime() - (2 * DATEADJUSTMENT) - ZONEERROR)
var nowDateObject = new Date(nowInMS)
This last code sample works accurately on all Navigator 3.0 platforms that I've been able to test by setting them to a variety of time zones around the world.
JavaScript makes one departure from the Java Date object that could have a negative impact on scripts using dates that reach to the year 2000 and beyond. This isn't a "Year 2000" problem per se, but you need to script around the problem just the same, at least until the problem is fixed in a future release.
At issue is the way JavaScript treats the year component of a Date object. According to the Java specification, years are integers after the year 1900. In other words, if you use the getYear() method for a Date object that holds a 1997 date, the returned value should be 97. This is how both Java and JavaScript work for dates prior to 2000.
Java, however, remains true to the algorithm into the future, where the year 2001 is represented by 101. JavaScript, however, treats all years beginning with 2000 as the actual year value: 2001 is 2001. This jump in values could trip up a script that relies on a returned year value in sequence with years prior to 2000.
I prefer to do away with ambiguities such as starting the year counts with 1900. Therefore, I recommend processing all values that come from the getYear() method through a filter function that adds 1900 to any returned value that is less than 100. The good news is that the setYear() method and all Date object constructors that take an integer value for the year accurately handle four-digit numbers. Use 'em.
ONE LAST COMPATIBILITY TIDBIT
While I have not performed exhaustive tests on all Navigator platforms, the most intelligent implementation I've seen is on the Windows 95 platform. At least for North America, if you specify in the Date/Time control panel that you want Windows 95 to automatically adjust your clock for Daylight Saving, your Date object handling gets an added bonus: The correct offset to GMT is calculated for you for any date throughout the year.
For example, Pacific Standard Time is eight hours earlier than GMT, while Pacific Daylight Time is seven hours earlier. If you live in the Pacific time zone and create a Date object for local noon in December (when standard time is in effect), the GMT equivalent is set to 8:00 P.M. If you create a Date object for noon on some day in August, though, the GMT equivalent is properly set to 7:00 P.M.wno matter what time of year the script runs to create that object. This is very smart.
In contrast, the GMT offset for Daylight Saving time on the Mac OS is governed by the current state of the system clock -- whether or not Daylight Saving time is turned on. Therefore, if you are in the winter months in North America and create a Date object for a July 4 fireworks event at 9:00 P.M., the GMT time will be calculated on the winter months' standard time offset. When July comes around, the Daylight Saving conversion will display the event as starting at 10:00 P.M.--too bad you missed the show.
It can be fun to experiment with changes to the control panels and their effects on the Date object. Be aware, however, that Navigator 3 picks up information about the client's clock setting (and time zone offset) when it launches. If you make a change to the control panel, you must quit and relaunch Navigator for the changes to affect your script experiments.
DATE OBJECT VALIDATION
It may be tempting to use the Date object as a way to help validate date and time entries in forms whose fields get submitted to CGI programs for further processing. What's easy to forget, however, is that there are numerous accepted date and time formats in use around the world. Unless you've used software that's been localized for other countries, you probably haven't seen the large variety of formats currently in use. In the United States, for example, the short date format is mm/dd/yyyy. In many other parts of the world, the month and date positions are switched. In still other parts of the world, there might be different delimiter characters between the components, such as a dash (-) or a period (.) instead of a slash (/). The same is true for time formats.
The JavaScript Date object is not smart enough to know whether 3/4/1997 is March 4, 1997, or April 3, 1997. Nor can any script that you write parse such a text field entry. And just because you supply a sample entry in your form or label doesn't mean that the visitor will follow it.
All of this leads me to suggest that you divide date and time entry fields into multiple components, each of which can be easily validated with JavaScript. For example, the following form, "Example Form," provides three fields for date entry, each of which is backed up by a healthy validation script to check for range and integers.
To view the source code for this form and validation functions, click here.
You can even do away with all the validation scripts entirely by setting up the data entry components as Select objects. You saw an example of this earlier in the table "Date Object Methods." With this scheme, there is no possible way for incorrect data to slip through.
Other options for date selection include Java calendar applets (in which the visitor navigates to the desired month, and clicks on the desired date) and even creating dynamic calendars with JavaScript in separate frames. Let your page design rule the best way to evoke a date or time entry from visitors.
SUMMARY
As this lengthy examination of the Date object implies, scripting dates and times is not necessarily the easiest task imaginable for enhancing your Web pages. But that might signal an opportunity to help users navigate dates and times in a way that distinguishes your pages from the crowd. For example, I push the envelope a bit in some parts of my own Web site by combining date conversions and arithmetic along with the HTTP cookie. I do this to point out all areas of a page that have been updated since the visitor's last time at the pageweven though I may have been through several update cycles in the meantime.
To accomplish an application such as this requires in-depth study of the Date object and lots of experimentation with clock and time zone settings of Navigator client platforms. I'm convinced, however, that there are many great ideas out there waiting to be discovered for want of an understanding of the Date object. If this article inspires you, let View Source know what you've accomplished. We'll be glad to point the world to your masterpiece.
MEMBERS RESOURCES:
BOOKS
View Source wants your feedback!
Write to us and let us
know what you think of this article.
Author and consultant Danny Goodman's 25th book is the JavaScript Bible, the updated 2d edition of his bestselling JavaScript Handbook, published by IDG Books. His next title, arriving at the end of February 1997, is The Official Marimba Guide to Bongo, published by Sams.net.
(3.97)
For the latest technical information on Sun-Netscape Alliance products, go to: http://developer.iplanet.com
For more Internet development resources, try Netscape TechSearch.