Perhaps you need to calculate a date at some fixed number of days or weeks in the future or figure out the number of days between two dates.. Here is an example that establishes some pra
Trang 1Date and time arithmetic
You may need to perform some math with dates for any number of reasons Perhaps you need to calculate a date at some fixed number of days or weeks in the future or figure out the number of days between two dates When calculations of
these types are required, remember the lingua franca of JavaScript date values:
milliseconds
What you may need to do in your date-intensive scripts is establish some variable values representing the number of milliseconds for minutes, hours, days, or weeks, and then use those variables in your calculations Here is an example that establishes some practical variable values, building on each other:
var oneMinute = 60 * 1000 var oneHour = oneMinute * 60 var oneDay = oneHour * 24 var oneWeek = oneDay * 7
With these values established in a script, I can use one to calculate the date one week from today:
var targetDate = new Date() var dateInMs = targetDate.getTime() dateInMs += oneWeek
targetDate.setTime(dateInMs)
Another example uses components of a date object to assist in deciding what kind of greeting message to place in a document, based on the local time of the user’s PC clock Listing 36-2 adds to the scripting from Listing 36-1, bringing some quasi-intelligence to the proceedings Again, this script uses the older array creation mechanism to be compatible with Navigator 2 and Internet Explorer 3
Listing 36-2: A Dynamic Welcome Message
<HTML>
<HEAD>
<TITLE>Date String Maker</TITLE>
<SCRIPT LANGUAGE=”JavaScript”>
function MakeArray(n) { this.length = n return this }
monthNames = new MakeArray(12) monthNames[1] = “January”
monthNames[2] = “February”
monthNames[3] = “March”
monthNames[4] = “April”
monthNames[5] = “May”
monthNames[6] = “June”
monthNames[7] = “July”
monthNames[8] = “August”
monthNames[9] = “September”
monthNames[10] = “October”
monthNames[11] = “November”
monthNames[12] = “December”
Trang 2dayNames = new MakeArray(7)
dayNames[1] = “Sunday”
dayNames[2] = “Monday”
dayNames[3] = “Tuesday”
dayNames[4] = “Wednesday”
dayNames[5] = “Thursday”
dayNames[6] = “Friday”
dayNames[7] = “Saturday”
function customDateString(oneDate) {
var theDay = dayNames[oneDate.getDay() + 1]
var theMonth = monthNames[oneDate.getMonth() + 1]
var theYear = oneDate.getYear()
theYear += (theYear < 100) ? 1900 : 0
return theDay + “, “ + theMonth + “ “ + oneDate.getDate() + “, “ + theYear
}
function dayPart(oneDate) {
var theHour = oneDate.getHours()
if (theHour <6 )
return “wee hours”
if (theHour < 12)
return “morning”
if (theHour < 18)
return “afternoon”
return “evening”
}
</SCRIPT>
</HEAD>
<BODY>
<H1> Welcome!</H1>
<SCRIPT LANGUAGE=”JavaScript”>
today = new Date()
var header = (customDateString(today)).italics()
header += “<BR>We hope you are enjoying the “
header += dayPart(today) + “.”
document.write(header)
</SCRIPT>
<HR>
</BODY>
</HTML>
The script divides the day into four parts and presents a different greeting for
each part of the day The greeting that plays is based, simply enough, on the hour
element of a date object representing the time the page is loaded into the browser
Because this greeting is embedded in the page, the greeting does not change no
matter how long the user stays logged on to the page
Counting the days
You may find one or two more date arithmetic applications useful One displays
the number of shopping days left until Christmas (in the user’s time zone); the
other is a countdown timer to the start of the year 2100
Trang 3Listing 36-3 demonstrates how to calculate the number of days between the current day and some fixed date in the future The assumption in this application is that all calculations take place in the user’s time zone The example shows the display of the number of shopping days before the next Christmas day (December 25) The basic operation entails converting the current date and the next December
25 to milliseconds, calculating the number of days represented by the difference in milliseconds If you let the millisecond values represent the dates, JavaScript auto-matically takes care of leap years
The only somewhat tricky part is setting the year of the next Christmas day correctly You can’t just slap the fixed date with the current year, because if the program is run on December 26, the year of the next Christmas must be incremented
by one That’s why the constructor for the Christmas date object doesn’t supply a fixed date as its parameters but, rather, sets individual components of the object
Listing 36-3: How Many Days Until Christmas
<HTML>
<HEAD>
<TITLE>Christmas Countdown</TITLE>
<SCRIPT LANGUAGE=”JavaScript”>
function getDaysUntilXmas() { var oneMinute = 60 * 1000 var oneHour = oneMinute * 60 var oneDay = oneHour * 24 var today = new Date() var nextXmas = new Date() nextXmas.setMonth(11) nextXmas.setDate(25)
if (today.getMonth() == 11 && today.getDate() > 25) { nextXmas.setFullYear(nextXmas.getFullYear() + 1) }
var diff = nextXmas.getTime() - today.getTime() diff = Math.floor(diff/oneDay)
return diff }
</SCRIPT>
</HEAD>
<BODY>
<H1>
<SCRIPT LANGUAGE=”JavaScript”>
var header = “You have <I>” + getDaysUntilXmas() + “</I> “ header += “shopping days until Christmas.”
document.write(header)
</SCRIPT>
</H1><HR>
</BODY>
</HTML>
Trang 4The second variation on calculating the amount of time before a certain event
takes time zones into account For this demonstration, the page is supposed to
display a countdown timer to the precise moment when the flame for the 2004
Summer Games in Athens is to be lit That event takes place in a time zone that
may be different from that of the page’s viewer, so the countdown timer must
calculate the time difference accordingly
Listing 36-4 shows a simplified version that simply displays the ticking timer in
a text field The output, of course, could be customized in any number of ways,
depending on the amount of dynamic HTML you want to employ on a page The
time of the lighting for this demo is set at 17:00 GMT on August 13, 2004 (the date
is certainly accurate, but the officials may set a different time closer to the actual
event)
Because this application is implemented as a live ticking clock, the code starts
by setting some global variables that should be calculated only once so that the
function that gets invoked repeatedly has a minimum of calculating to do (to be
more efficient) The Date.UTC()method provides the target time and date in
standard time The getTimeUntil()function accepts a millisecond value (as
provided by the targetDatevariable) and calculates the difference between the
target date and the actual internal millisecond value of the client’s PC clock
The core of the getCountDown()function peels off the number of whole days,
hours, minutes, and seconds from the total number of milliseconds difference
between now and the target date Notice that each chunk is subtracted from the
total so that the next smaller chunk can be calculated from the leftover milliseconds
One extra touch on this page is that users of Windows operating systems have a
display of the local date and time of the actual event The Mac is excluded because
it does not provide accurate daylight saving time adjustments for local dates Some
UNIX flavors may do the right thing, but they were not tested for this example
Listing 36-4: Summer Games Countdown
<HTML>
<HEAD>
<TITLE>Summer Games Countdown</TITLE>
<SCRIPT LANGUAGE=”JavaScript”>
// globals calculate only once
// set target date to 1700GMT on August 13, 2004
var targetDate = Date.UTC(2004, 7, 13, 17, 0, 0, 0)
var oneMinute = 60 * 1000
var oneHour = oneMinute * 60
var oneDay = oneHour * 24
function getTimeUntil(targetMS) {
var today = new Date()
var diff = targetMS - today.valueOf()
return Math.floor(diff)
}
function getCountDown() {
var ms = getTimeUntil(targetDate)
var output = “”
var days, hrs, mins, secs
if (ms >= 0) {
Continued
Trang 5Listing 36-4 (continued)
days = Math.floor(ms/oneDay)
ms -= oneDay * days hrs = Math.floor(ms/oneHour)
ms -= oneHour * hrs mins = Math.floor(ms/oneMinute)
ms -= oneMinute * mins secs = Math.floor(ms/1000) output += days + “ Days, “ +
hrs + “ Hours, “ + mins + “ Minutes, “ + secs + “ Seconds”
} else { output += “The time has passed.”
} return output }
function updateCountDown() { document.forms[0].timer.value = getCountDown() setTimeout(“updateCountDown()”, 1000)
}
</SCRIPT>
</HEAD>
<BODY onLoad=”updateCountDown()”>
<H1>Athens Games Torch Lighting Countdown</H1>
<P>
<SCRIPT LANGUAGE=”JavaScript”>
if (navigator.userAgent.indexOf(“Win”) >= 0) { document.write(“(“ + (new Date(targetDate)).toLocaleString()) document.write(“ in your time zone.)”)
}
</SCRIPT>
</P>
<FORM>
<INPUT TYPE=”text” NAME=”timer” SIZE=60>
</FORM>
<HR>
</BODY>
</HTML>
Date bugs and gremlins
Each new browser generation improves the stability and reliability of scripted date objects Unfortunately, Navigator 2 has enough bugs and crash problems across many platforms to make scripting complex world-time applications for this browser impossible The Macintosh version also has bugs that throw off dates by
as much as a full day I recommend avoiding NN2 on all platforms for serious date and time scripting
Trang 6The situation is much improved for NN3 Still, some bugs persist One bug in
particular affects Macintosh versions of NN3 Whenever you create a new date
object with daylight saving time engaged in the Date and Time control panel, the
browser automatically adds one hour to the object See the time-based application
in Chapter 54 for an example of how to counteract the effects of typical time bugs
Also afflicting the Macintosh in NN3 is a faulty calculation of the time zone offset for
all time zones east of GMT Instead of generating these values as negative numbers
(getting lower and lower as you head east), the offset values increase continuously
as you head west from Greenwich While the Western Hemisphere is fine, the values
continue to increase past the international date line, rather than switch over to the
negative values
Internet Explorer 3 isn’t free of problems It cannot handle dates before January
1, 1970 (GMT) Attempts to generate a date before that one result in that base date
as the value IE3 also completely miscalculates the time zone offset, following the
erroneous pattern of Navigator 2 Even Navigators 3 and 4 have problems with
historic dates You are asking for trouble if the date extends earlier than January 1,
A.D 1 Internet Explorer 4, on the other hand, appears to sail very well into ancient
history
You should be aware of one more discrepancy between Mac and Windows
versions of Navigator through Version 4 In Windows, if you generate a date object
for a date in another part of the year, the browser sets the time zone offset for that
object according to the time zone setting for that time of year On the Mac, the
current setting of the control panel governs whether the normal or daylight saving
time offset is applied to the date, regardless of the actual date within the year This
discrepancy affects Navigator 3 and 4 and can throw off calculations from other
parts of the year by one hour
It may sound as though the road to Dateobject scripting is filled with land mines
While date and time scripting is far from hassle free, you can put it to good use with
careful planning and a lot of testing
Validating Date Entries in Forms
Given the bug horror stories in the previous section, you may wonder how you
can ever perform data entry validation for dates in forms The problem is not so
much in the calculations as it is in the wide variety of acceptable date formats
around the world No matter how well you instruct users to enter dates in a
particular format, many will follow their own habits and conventions Moreover,
how can you know whether an entry of 03/04/2002 is the North American March 4,
2002, or the European April 3, 2002? The answer: You can’t
My recommendation is to divide a date field into three components: month, day,
and year Let the user enter values into each field and validate each field individually
for its valid range Listing 36-5 shows an example of how this is done The page
includes a form that is to be validated before it is submitted Each component field
does its own range checking on the fly as the user enters values But because this
kind of validation can be defeated, the page includes one further check triggered by
the form’s onSubmitevent handler If any field is out of whack, the form submission
is canceled
Trang 7Listing 36-5: Date Validation in a Form
<HTML>
<HEAD>
<TITLE>Date Entry Validation</TITLE>
<SCRIPT LANGUAGE=”JavaScript”>
<! // **BEGIN GENERIC VALIDATION FUNCTIONS**
// general purpose function to see if an input value has been entered at all function isEmpty(inputStr) {
if (inputStr == “” || inputStr == null) { return true
} return false }
// function to determine if value is in acceptable range for this application function inRange(inputStr, lo, hi) {
var num = parseInt(inputStr, 10)
if (num < lo || num > hi) { return false
} return true }
// **END GENERIC VALIDATION FUNCTIONS**
function validateMonth(field, bypassUpdate) { var input = field.value
if (isEmpty(input)) { alert(“Be sure to enter a month value.”) select(field)
return false } else {
input = parseInt(field.value, 10)
if (isNaN(input)) { alert(“Entries must be numbers only.”) select(field)
return false } else {
if (!inRange(input,1,12)) { alert(“Enter a number between 1 (January) and 12 (December).”) select(field)
return false }
} }
if (!bypassUpdate) { calcDate() }
return true }
function validateDate(field) { var input = field.value
Trang 8if (isEmpty(input)) {
alert(“Be sure to enter a date value.”)
select(field)
return false
} else {
input = parseInt(field.value, 10)
if (isNaN(input)) {
alert(“Entries must be numbers only.”)
select(field)
return false
} else {
var monthField = document.birthdate.month
if (!validateMonth(monthField, true)) return false
var monthVal = parseInt(monthField.value, 10)
var monthMax = new Array(31,31,29,31,30,31,30,31,31,30,31,30,31)
var top = monthMax[monthVal]
if (!inRange(input,1,top)) {
alert(“Enter a number between 1 and “ + top + “.”) select(field)
return false }
}
}
calcDate()
return true
}
function validateYear(field) {
var input = field.value
if (isEmpty(input)) {
alert(“Be sure to enter a year value.”)
select(field)
return false
} else {
input = parseInt(field.value, 10)
if (isNaN(input)) {
alert(“Entries must be numbers only.”)
select(field)
return false
} else {
if (!inRange(input,1900,2005)) {
alert(“Enter a number between 1900 and 2005.”) select(field)
return false }
}
}
calcDate()
return true
}
function select(field) {
field.focus()
field.select()
}
Continued
Trang 9Listing 36-5 (continued)
function calcDate() { var mm = parseInt(document.birthdate.month.value, 10) var dd = parseInt(document.birthdate.date.value, 10) var yy = parseInt(document.birthdate.year.value, 10) document.birthdate.fullDate.value = mm + “/” + dd + “/” + yy }
function checkForm(form) {
if (validateMonth(form.month)) {
if (validateDate(form.date)) {
if (validateYear(form.year)) { return true
} } } return false }
// >
</SCRIPT>
</HEAD>
<BODY>
<FORM NAME=”birthdate” ACTION=”mailto:fun@dannyg.com” METHOD=POST onSubmit=”return checkForm(this)”>
Please enter your birthdate <BR>
Month:<INPUT TYPE=”text” NAME=”month” VALUE=1 SIZE=2 onChange=”validateMonth(this)”>
Date:<INPUT TYPE=”text” NAME=”date” VALUE=1 SIZE=2 onChange=”validateDate(this)”>
Year:<INPUT TYPE=”text” NAME=”year” VALUE=1900 SIZE=4 onChange=”validateYear(this)”>
<P>
Thank you for entering:<INPUT TYPE=”text” NAME=”fullDate” SIZE=10><P>
<INPUT TYPE=”submit”> <INPUT TYPE=”Reset”>
</FORM>
</BODY>
</HTML>
The page shows the three entry fields as well as a field that is normally hidden
on a form to be submitted to a CGI program On the server end, the CGI program responds only to the hidden field with the complete date, which is in a format for entry into, for example, an Informix database
Not every date entry validation must be divided in this way For example, an intranet application can be more demanding in the way users are to enter data Therefore, you can have a single field for date entry, but the parsing required for such a validation is quite different from that shown in Listing 36-5 See Chapter 43 for an example of such a one-field date validation routine
Trang 10The Array Object
An array is the sole JavaScript data structure provided
for storing and manipulating ordered collections of
data But unlike some other programming languages,
JavaScript’s arrays are very forgiving as to the kind of data
you store in each cell or entry of the array This allows, for
example, an array of arrays, providing the equivalent of
multi-dimensional arrays customized to the kind of data your
appli-cation needs
If you have not done a lot of programming in the past, the
notion of arrays may seem like an advanced topic But if you
ignore their capabilities, you set yourself up for a harder job
when implementing many kinds of tasks Whenever I
approach a script, one of my first thoughts is about the data
being controlled by the application and whether handling it as
an array will offer some shortcuts for creating the document
and handling interactivity with the user
I hope that by the end of this chapter, you will not only be
familiar with the properties and methods of JavaScript arrays,
but you will begin to look for ways to make arrays work for you
Structured Data
In programming, an array is defined as an ordered
collec-tion of data You can best visualize an array as a table, not
much different from a spreadsheet In JavaScript, arrays are
limited to a table holding one column of data, with as many
rows as needed to hold your data As you have seen in many
chapters in Part III, a JavaScript-enabled browser creates a
number of internal arrays for the objects in your HTML
docu-ments and browser properties For example, if your document
contains five links, the browser maintains a table of those
links You access them by number (with 0 being the first link)
in the array syntax: the array name is followed by the index
number in square brackets, as in document.links[0], which
represents the first link in the document
For many JavaScript applications, you will want to use an
array as an organized warehouse for data that users of your
page access, depending on their interaction with form
ele-ments In the application shown in Chapter 50, for example, I
demonstrate an extended version of this usage in a page that
lets users search a small table of data for a match between the
37
In This Chapter
Working with ordered collections of data
Simulating multidimensional arrays
Manipulating information stored in
an array