reptile7's JavaScript blog
Wednesday, January 30, 2008
 
Around the World in 80 Milliseconds, Part 1
Blog Entry #102

Digital clock scripts previously covered in HTML Goodies' JavaScript Script Tips:
(1) The Script Tips #25-28 Script (see Blog Entry #61)
(2) The Script Tips #84-86 Script (see Blog Entry #101)

So, what time is it in London? What time is it in Auckland, where the Kiwis are currently observing daylight-saving time? Script Tips #87, #88, #89, and #90 discuss a "World Clock" script that generates

hour:minute:second month date, year (e.g., 15:15:00 Jan 26, 2008)

time/date displays for London, Auckland, and eight other locations around the world, and can easily be extended to generate corresponding displays for all forty time zones. We'll spend the next several posts analyzing the Script Tips #87-90 Script, which can be accessed by following the Here's the Code links in all four script tips (but not by following the accompanying "Open Code in a New Window" links, which lead to a blank page) and is reproduced in the div below:

<script language="javascript">

<!--

var adjust = 0;
var zone = " New Orleans"; // This is the text that first appears denoting time zone
gmtOffset = 360; // Setting this to your offset will start with your current time
var PST = 480;
var EST = 300;
var TK = -540;
var HW = 600;
var LD = 0;
var MX = 360;
var HK = -480;
var FJ = -720;
var ND = -330;

function checkPST( ) {
clearTimeout(checkDateTime);
gmtOffset = eval(PST + adjust);
zone = " Pacific";
checkDateTime( );
}

function checkEST( ) {
clearTimeout(checkDateTime);
gmtOffset = EST + adjust;
zone = " Eastern";
checkDateTime( );
}

function checkTK( ) {
clearTimeout(checkDateTime);
gmtOffset = TK + adjust;
zone = " Tokyo";
checkDateTime( );
}

function checkHW( ) {
clearTimeout(checkDateTime);
gmtOffset = HW + adjust;
zone = " Hawaii";
checkDateTime( );
}

function checkLD( ) {
clearTimeout(checkDateTime);
gmtOffset = LD + adjust;
zone = " London(GMT)";
checkDateTime( );
}

function checkHK( ) {
clearTimeout(checkDateTime);
gmtOffset = HK + adjust;
zone = " Hong Kong";
checkDateTime( );
}

function checkFJ( ) {
clearTimeout(checkDateTime);
gmtOffset = FJ + adjust;
zone = " Auckland";
checkDateTime( );
}

function checkMX( ) {
clearTimeout(checkDateTime);
gmtOffset = MX + adjust;
zone = " Mexico City";
checkDateTime( );
}

function checkND( ) {
clearTimeout(checkDateTime);
gmtOffset = ND + adjust;
zone = " New Delhi";
checkDateTime( );
}

function checkDateTime( ) {

adjust = 0;

var today = new Date( );
var year = today.getFullYear( );
var month = today.getMonth( ) + 1;
var date = today.getDate( );
var day = today.getDay( );
var hour = today.getHours( );
var minute = today.getMinutes( );
var second = today.getSeconds( );

// This next tidbit gets the last Saturday in the month of Oct, for daylight-saving time purposes
var lastSat;
lastSat = date - (day + 1);
while (lastSat < 32) {
lastSat += 7;
}

if (lastSat > 31) lastSat += -7;

// This bit grabs the first Saturday in April for the start of daylight time
var firstSat;
firstSat = date - (day + 1);
while (firstSat > 0) {
firstSat += -7;
}

if (firstSat < 1) firstSat += 7;

// Adjust for Windows95 daylight-saving time changes
if ((((month == 4) && (date >= firstSat)) || month > 4) && (month < 11 || ((month == 10) && day <= lastSat))) { adjust += 60; }

yourOffset = (new Date( )).getTimezoneOffset( );
yourOffset = yourOffset + adjust;
ourDifference = eval(gmtOffset - yourOffset);
var half = eval(ourDifference % 60);
ourDifference = Math.round(ourDifference / 60);
hour = eval(hour - ourDifference);

var m = new Array("mm", "Jan.", "Feb.", "Mar.", "Apr.", "May ", "Jun.", "Jul.", "Aug.", "Sept.", "Oct.", "Nov.", "Dec.");

var leap = eval(year % 4); 

if ((half == -30) || (half == 30)) minute += 30;
if (minute > 59) minute -= 60, hour++;
if (minute < 0) minute += 60, hour--;
if (hour > 23) hour -= 24, date += 1;
if (((month == 4) || (month == 6) || (month == 9) || (month == 11)) && (date == 31))

date = 1, month += 1;
if (((month == 2) && (date > 28)) && (leap != 0)) date = 1, month += 1;
if ((month == 2) && (date > 29)) date = 1, month += 1;
if (hour < 0) hour += 24, date -= 1;
if ((date == 32) && (month == 12)) month = m[1], date = 1, year += 1;
if (date == 32) date = 1, month += 1;
if ((date < 1) && (month == 1)) month = m[12], date = 31, year -= 1;
if (date < 1) date = 31, month -= 1;
if (((month == 4) || (month == 6) || (month == 9) || (month == 11)) && (date == 31))

date = 30;
if ((month == 2) && (date > 28)) date = 29;
if (((month == 2) && (date > 28)) && (leap != 0)) date = 28;
for (i = 1; i < 13; i++) {
if (month == i) { month = m[i];
break;
}
}

// var dateTime = "" + ((hour >12) ? hour -12 :hour)
var dateTime = hour;
dateTime = ((dateTime < 10) ? "0" : "") + dateTime;
dateTime = " " + dateTime;
dateTime += ((minute < 10) ? ":0" : ":") + minute;
dateTime += ((second < 10) ? ":0" : ":") + second;
// dateTime += (hour >= 12) ? " pm," : " am, ";
dateTime += " " + month + " " + date + ", " + year;
document.clock.face.value = dateTime;
document.clock.locationx.value = zone;
setTimeout("checkDateTime( );", 900);
}

// End -->

</script>

<!-- The HTML Portion Starts Here -->

<body text="#000000" bgcolor="#000000" onload="checkDateTime( );"> 

<center><b><font color="#ffffff" size="+2">World Clock</font></b>
<form name="clock">
<center>
<input type="text" name="face" size="31" value="" /><br /><br />
<font color="#ffffff">
<b>Current Time Zone</b>
</font>
<br /><input type="text" name="locationx" size="21" value="" />
</center><br />
<table border="1" cellpadding="5"><tr>
<td><input type="button" name="reset" value="Pacific" onclick="checkPST( );" /></td>
<td><input type="button" name="reset" value="Eastern" onclick="checkEST( );" /></td>
<td><input type="button" name="reset" value="Auckland" onclick="checkFJ( );" /></td></td><td>
<td><input type="button" name="reset" value="Tokyo " onclick="checkTK( );" /></td>
<td><input type="button" name="reset" value="London" onclick="checkLD( );" /></td>
<td><input type="button" name="reset" value="Hong Kong" onclick="checkHK( );" /></td></tr><tr>
<td><input type="button" name="reset" value="Hawaii" onclick="checkHW( );" /></td>
<td><input type="button" name="reset" value="Mexico" onclick="checkMX( );" /></td>
<td><input type="button" name="reset" value="New Delhi" onclick="checkND( );" /></td>

</tr></table> 

</form>

</center>

Joe provides a demo page for the Script Tips #87-90 Script here.

The script display

Let's briefly run through the Script Tips #87-90 Script document body; we'll first consider the body's structure and then we'll replace its presentational aspects with a style sheet.

At the top of the script's display is a large "World Clock" string, which is not but should be marked up as a heading:

<h2>World Clock</h2>
<!-- Don't like skipping heading levels? Mark it up as an h1 element if you prefer. -->

The rest of the script's display is wrapped in a <form name="clock"> ... </form> container*. The clock form begins with a text field, named face (not text, as incorrectly stated in Script Tip #87), to which the above time/date string will be written. Below the face field is a "Current Time Zone" string, which serves as a label for a second text field, named locationx, that will hold a string indicating a time zone location (Pacific, Eastern, etc.) in which the time is that shown in the face field. For example, the script is set up to initially display U.S. Central Time in the face field; the corresponding locationx string is New Orleans.

*As noted in previous entries, for backward-compatibility and validation reasons you might want to:
(a) Remove the form's name="clock" attribute, which is deprecated by XHTML 1.0;
(b) Give the form an action="" attribute - the action attribute has a #REQUIRED default value designation; and then
(c) Use document.forms[0] to reference the form in the script's script element.

Because the "Current Time Zone" string is a control label, let's mark it up that way, shall we?

<label for="ctz">Current Time Zone</label><br />
<input id="ctz" name="locationx" size="21" />

The type="text" and value="" attributes of the face and locationx input elements are unnecessary and can be removed, but there's nothing 'tref' about them, either.

Below the locationx field is a set of nine push buttons that is laid out via a three-row table. Each button is labeled, via its value attribute, with a time zone location. When clicked, each button triggers functions that generate face and locationx field values appropriate for that button.

Validation note: Each button is also equipped with a name=reset attribute that never comes into play and would seem to be removable; however, the W3C's (X)HTML DTDs state (here, e.g.), [For the input element,] the name attribute is required for all but [type=]submit & reset, so perhaps we should let the button name attributes be (for XHTML-compliance, however, their values and the other unquoted attribute values should be quoted, per the div above).

There's no geographical relationship between the buttons of a given table row or column, and because the W3C holds that [t]ables should not be used purely as a means to layout document content as this may present problems when rendering to non-visual media, I thought, "Maybe we should replace the script's table with a div element container." But of course, there's nothing stopping us from grouping the buttons geographically - we could have a 'U.S.' row (Pacific, Mountain, Central, Eastern), a 'European' row (London, Berlin, Athens, Moscow), etc. - and I am now inclined to keep the table scaffolding in place.

One more point before moving on: On the "Here's the Code" page, the first table row's tr element end-tag and the second table row's tr element start-tag are mistyped as </td> and <td>, respectively; the correct </tr> and <tr> tags appear in the demo page's source, however.

And now for some style...

The script's body element is equipped with deprecated text="#000000" and bgcolor="#000000" attributes that produce black text on a black background - how's that for a color scheme, huh? On the other hand, the script document's two 'text nodes' - the "World Clock" and "Current Time Zone" strings - are wrapped in deprecated <font color="#ffffff"> ... </font> containers. Accordingly, let's remove the text/bgcolor/color attributes and begin our style sheet with:

body { background-color: black; color: white; }

The body content is centered by, curiously, two nested center elements, which are deprecated and can be replaced as follows:
(1) For centering the pre-table content:
(a) Wrap the face field, the "Current Time Zone" label, and the locationx field in a div element:

<div>
<input name="face" size="31" /><br /><br />
<label for="ctz">Current Time Zone</label><br />
<input id="ctz" name="locationx" size="21" />
</div>

(b) Give the div element and the "World Clock" h# element (vide supra) a common class="cent" attribute; and
(c) Add the following rule to the style sheet:

.cent { text-align: center; }

(It's not entirely clear whether the CSS text-align property is meant to be applicable to non-text elements, but the text-align definition - This property describes how inline content of a block is aligned - would certainly seem to apply to the input and label elements, which are inline elements.)

(2) Center the table with the following style rule set:

table { margin-left: auto; margin-right: auto; }

Style-wise, the b element/size="+2" markup that surrounds the "World Clock" string in the original script is reproduced by the h# container. To bold(en) the "Current Time Zone" label, you can add to the style sheet:

label { font-weight: bold; }

Finally, the HTML 4 Index of Attributes shows that neither the border="1" attribute nor the cellpadding="5" attribute of the table element is deprecated, but it is simple enough to replace these attributes with the style rules below:

table, td { border: 1px solid white; }
td { padding: 5px; }

Deconstruction intro

When the script document has loaded, the body element's onload="checkDateTime( );" attribute calls the large checkDateTime( ) function in the script element in the document head.

function checkDateTime( ) {
adjust = 0;

The first checkDateTime( ) statement assigns 0 to the variable adjust, which will come into play during the daylight-saving time part of the script; this statement is unnecessary because adjust is globally initialized with a value of 0 at the top of the script element:

<script type="text/javascript">
<!--
var adjust = 0;

Back to the checkDateTime( ) function - we next grab some date and time values:

var today = new Date( );
var year = today.getFullYear( );
var month = today.getMonth( ) + 1;
var date = today.getDate( );
var day = today.getDay( );
var hour = today.getHours( );
var minute = today.getMinutes( );
var second = today.getSeconds( );

(I gave you the following references in the previous post, but let me give them to you again: Mozilla discusses the Date object here and collects links to its pages for the above Date object methods here.)

As noted earlier, the Script Tips #87-90 Script initially displays U.S. Central Time in the face text box; however, the preceding block of code returns local date and time information for the user's machine, whether the user is in Seattle, Sarajevo, or Sydney. This is a good time to bring up a general caveat about clock scripts that Joe raised at the beginning of Script Tip #85: As long as the viewer has his or her browser [clock] set correctly, you're good to go. If they don't, well, you can't help that.

Next we have three blocks of code dealing with daylight-saving time. Ah, daylight-saving time...any objective reader of Wikipedia's DST entry would conclude that DST's costs are definite and its benefits are questionable...anyway, the script's DST code deserves its own entry and we'll get to it next time.

reptile7

Comments: Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)