reptile7's JavaScript blog
Saturday, February 23, 2019
 
Times of Our World, Part 2
Blog Entry #394

Let's get back now to our discussion of the Java/JavaScript Goodies World Clock -- Daylight Savings Time. In the previous entry we ticked through the dstclock clock's foundational JavaScript, preceding metatext, and HTML scaffolding; in today's post we'll unwind the clock's day and time displays. Three Clock origin values are relevant to those displays:
(w) the week string,
(h) the hour number, and
(m) the min string.

Day prep

The week string contains a three-letter day-of-the-week abbreviation plus a comma:
Sun, or Mon, or ... Sat,

The week day is the day in London. If it's Sunday in London, then it could be Monday in Sydney or Saturday in San Francisco, so we'll need to determine the week + 1 and week - 1 days in addition to the week day. Toward this end, Stuart initially maps the week value to an index number that's one higher than the corresponding getDay( ) index:

if (week == "Sun,") { week = 1; }
if (week == "Mon,") { week = 2; }
if (week == "Tue,") { week = 3; }
if (week == "Wed,") { week = 4; }
if (week == "Thu,") { week = 5; }
if (week == "Fri,") { week = 6; }
if (week == "Sat,") { week = 7; }


The week index is subsequently lowered by one for those users who are using a Macintosh:

// Fix Mac version Communicator bug
function checkOS( ) {
    if (navigator.appVersion.indexOf("Mac") > 0) return "Macintosh";
    else return "other"; }
var check = checkOS( );
if (check == "Macintosh") { week -= 1; }


• If there's a reason to use this code rather than just an
if (navigator.appVersion.indexOf("Mac") > 0) week -= 1;
statement, I don't see it.

• I myself would have reached for the navigator object's platform property to flag Macintosh users (not that we want to be 'platform sniffing' if we can help it, of course) although it is clear from JavaScript Kit's Navigator Object page - see its Additional browsers' Navigator Information subsection - that a navigator.appVersion probe will do the job.

There's no mention of any Macintosh support issues in the toGMTString( ) entry in the JavaScript 1.2 Reference. As Netscape's Communicator/Navigator 4.0-4.05 browsers have a JavaScript 1.2 engine, I downloaded Navigator 4.05 for the Macintosh from OldVersion.com and ran a
window.alert((new Date(2019, 2, 1)).toGMTString( ).split(" ")[0]);
command with it, and the alert( ) box that popped up had a Sat, on it even though 1 March 2019 is a Friday, so there you have it.

We're almost ready to start writing out the locale displays: the missing link between
the full day names in those displays and
the week index
is a weekly Array whose creation follows the checkOS( ) check.

weekly = new Array("Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday");

(Nine elements? Duplicate Saturday and Sunday values? We'll clean this up later.)

More yesteryear matters, in the name of completeness

In his authorship comment, Stuart states that the dstclock code only works with Communicator 4, but should be gracefully ignored by lesser browsers. Actually, there are no features in the dstclock code that are specific to JavaScript 1.2, and I find that
if the week string → index number conversions are formulated as an if ... else if ... else if ... chain
AND
if the script language="javascript1.2" attributes are converted to language="javascript" attributes
then Navigator 3.04, which has a JavaScript 1.1 engine, runs the code without incident.

A bit of sleuthing, with some help from this Netscape onError example, revealed that Navigator 3.04 can't handle the original code for an unexpected reason: once an index number is assigned to week, the subsequent (elseless)
if (week == "Three-letter-day-of-the-week-abbreviation,") comparison
throws a "[day] is not a number" type-conversion error that propagates through the rest of the <script>. If the date object's day is a Friday, then no errors are thrown because the final
if (week == "Sat,") week = 7;
conditional is operative, as it would be for Navigator 4.05 (vide supra).

As it happens, however, Navigator 4.05 and Navigator 3.04 have another toGMTString( ) problem besides the dayday + 1 problem: the time part of the toGMTString( ) return gives the local time and not the time in London, or at least that's what I see in the SheepShaver environment on my computer, for example, for my California locale (GMT - 8)
(new Date(2019, 2, 1)).toGMTString( )
returns
Sat, 02 Mar 2019 00:00:00 GMT
rather than
Fri, 01 Mar 2019 08:00:00 GMT.

Anyway, it's not worth my while (or your while, or anyone else's while) to determine whether the time discrepancy is a browser bug or a SheepShaver artifact in that we've spent enough time with these ancient browsers as it is and we really ought to be leaving them behind, yes? We assume the use of a current browser in the following sections.

If you're going to San Francisco

The day and time display show is ready to hit the road. Here's what we've got for San Francisco:

// In the dstclock head
var sf_hour = hour - 8;
var sf_week = week;
var sf_ampm = " a.m.";
if (sf_hour < 0) {
    sf_hour += 24;
    sf_week -= 1; }
if (sf_hour > 11) { sf_ampm = " p.m."; }
if (sf_hour > 12) { sf_hour -= 12; }
if (sf_hour == 0) { sf_hour = 12; }

<!-- In the dstclock body -->
<td align="center" valign="top"><script language="javascript1.2"><!-- Hide it
document.write("<b>San Francisco</b><br>");
document.write(weekly[sf_week] + "<br>");
document.write(sf_hour + ":" + min + sf_ampm + "<br>");
// --></script></td>


The var sf_hour = hour - 8; line subtracts 8 from the hour in London to give the sf_hour in San Francisco for standard time, but there's no - 7 subtraction for daylight saving time, which is a pretty serious omission given that DST lasts more than half the year.

The var sf_week = week; line gives San Francisco the same day index that London has. If the London hour is in the 0-7 range, then San Francisco will calendar-wise be a day behind London: in this situation
(h) the hour - 8 subtraction gives a negative sf_hour, which is converted to the corresponding 12-hour clock hour by + 24 and - 12 operations, and
(w) the sf_week index is pushed back by one.
The GMT-pegged sf_week is then plugged into the weekly[ ] Array to get the full day name of the display.

An appropriate a.m./p.m. indicator is derived from the sf_hour and assigned to an sf_ampm variable à la the metatext code; moreover, the sf_hour is set to 12 for the midnight to 1 a.m. hour this time.

The day and time, plus a <br> line break that separates them, are written to the San Francisco table cell via separate document.write( ) commands (the concluding <br> in the second write( ) command serves no purpose and can be removed); we'll replace those commands with a tdObject.innerHTML += localeString assignment in due course.

For the last eight hours of Saturday the weekly[sf_week] day is undefined for Mac users as
the initial week index is decreased from 1 for Sunday in London to 0 by the checkOS( ) check and
the initial sf_week index is decreased from 0 to -1 by the if (sf_hour < 0) { ... sf_week -= 1; } conditional;
commenting out the checkOS( ) check changes the undefined to the Saturday we want.
Heading east we've got some serious redundancy on our hands: the dstclock source contains
separate units of code for Denver, Memphis, and New York that
have different hour - x GMT offsets
and of course different variable names (den_hour, mem_hour, etc.)
but are otherwise identical to the above San Francisco code
followed by separate units of code for London, Paris, Moscow, Bangkok, Tokyo, and Sydney that are only slightly more different...
We'll consolidate the lot of it, and sort out the DST thing, in the next post.

Tuesday, February 05, 2019
 
Times of Our World
Blog Entry #393

HTML Goodies' JavaScript Script Tips #87, #88, #89, and #90 discuss a world clock - see it here, get its code here - that we dissected in detail in Blog Entries #102, #103, #104, #105, and #106.

The Java/JavaScript Goodies Calculators, Calendars, and Clocks sector similarly offers a World Clock -- Daylight Savings Time, which was built by Stuart Price and which we'll take on in today's post. Stuart christened his clock WorldTime 1.0DST but I will henceforth refer to it via its dstclock document name. The dstclock clock gives static day and 12-hour time displays for ten locales:
San Francisco, Denver, Memphis, New York, London, Paris, Moscow, Bangkok, Tokyo, and Sydney.
You can see the clock at the aforelinked dstclock.html page and get its code here.

The component parts of each day and time display - the day of the week, the current hour, the current minute, and an appropriate a.m./p.m. designation - are derived from a dateObject.toGMTString( ) return, which is somewhat unusual in that most authors would use more conventional Date methods - specifically, getDay( ), getHours( )*, and getMinutes( ) - to determine those parts. In JavaScript 1.3 toGMTString( ) was deprecated in favor of a new toUTCString( ) method; toGMTString( ) and toUTCString( ) give identical returns with all of the browsers on my computer. The dstclock clock code was written in 1997 during the JavaScript 1.2 era - its various script elements hold a language="javascript1.2" attribute - when toUTCString( ) was not available.
*However, we'll call on getHours( ) when we get the user's local time in the clock preface.

In practice, the dstclock clock works OK some of the time, but it runs into trouble on Saturday or at the p.m./a.m. boundary for some locales, and it actually doesn't deal with daylight saving time at all - we'll get into all of this below.

Clock origin

Our deconstruction begins in the dstclock head this time. The ultimate starting point for every JavaScript clock is the creation of a new Date object:

<head>
<script language="javascript1.2"><!-- Hide it
/* ...Long authorship comment by Stuart... */
var date = new Date( );


The new Date is given a date identifier, which rubs me the wrong way - date is too close to Date for my taste - but is nonetheless legit as date is neither a JavaScript reserved keyword nor a JavaScript future reserved keyword.

We next obtain the toGMTString( ) form of the date Date, and assign it to a timegmt variable.

var timegmt = date.toGMTString( );

The toGMTString( ) method was actually not so well supported back in the day - more on this later - but today it reliably gives the GMT time corresponding to a time - either the current time or some other time - on the user's machine and in a regular, cross-browser format. Accordingly, if you're a user in Escondido, California and it's five in the afternoon on 1 March 2019, then the timegmt string should be:

Sat, 02 Mar 2019 01:00:00 GMT

The timegmt data is subsequently split( ) at its space-character delimiters; the resulting Array of substrings is bound to a time_string variable.

time_string = timegmt.split(" ");

The first five time_string elements are given more descriptive variable names:

week = time_string[0];
day = time_string[1];
mon = time_string[2];
year = time_string[3];
hms = time_string[4];


The hms string - the time part of the toGMTString( ) return - is then split( ) into its hour, minute, and second components, which are bound to an hms_string variable.

hms_string = hms.split(":");

We'll need an hour number to produce the UTC+x locale displays, and subtracting 0 from the hms_string[0] hour value gives us that number, which is stored in a separate hour variable. The hms_string[1] minute value is analogously stored as a string in a min variable.

var hour = hms_string[0] - 0;
var min = hms_string[1];


At a later point we'll map the week string to a getDay( )-like day-of-the-week index; no subsequent use is made of day, mon, and year.

Clock preface

The dstclock page begins with a line of metatext that
includes a local time determination and
is coded by:

<body bgcolor="#ffffff">
<script language="javascript1.2"><!-- Hide me
var loc_hour = date.getHours( );
var loc_ampm = " a.m.";
if (loc_hour > 11) { loc_ampm = " p.m."; }
if (loc_hour > 12) { loc_hour -= 12; }
document.write("<font size='2'>Note: Your local time is </font>");
document.write(loc_hour + ":" + min + loc_ampm);
document.write("<font size='2'> Times listed in the following table are based on your computer's time. If your computer's clock is wrong, so is this table.</font>");
// --></script>


You don't need a detailed play-by-play for this, do you? Didn't think so. But let me make a few points:

• Is it necessary to JavaScriptically write( ) out the non-time part of the text? Is it desirable to shrink that part of the text with the <font size="2"> ... </font> sizing? No and no.

• We can use the ?: conditional operator to set the loc_ampm setting on one line, i.e., var loc_ampm = loc_hour > 11 ? " p.m." : " a.m.";.

• For the midnight to 1 a.m. hour, adding an if (! loc_hour) loc_hour = 12; conditional will make the loc_hour hour 12 rather than 0.

• A loc_hour hour in the 0 loc_hour 9 range won't have a leading 0 because it comes from getHours( ); a min minute in the 00 min 09 range will have a leading 0 because it comes from toGMTString( ).

Clock frame

In the dstclock.txt code the clock is laid out via a one-row, ten-cell table; for the dstclock.html demo the table row is broken into three rows holding four cells, four cells, and two cells, respectively.

<table cellspacing="10">
<tr><td align="center" valign="top">
<script language="javascript1.2"><!-- Hide it
document.write("<b>San Francisco</b><br>");
document.write( /* day and time expression */ );
// --></script></td>
...
<td align="center" valign="top">
<script language="javascript1.2"><!-- Hide it
document.write("<b>Sydney</b><br>");
document.write( /* day and time expression */ );
// --></script></td></tr>
</table>


Can we alternatively use a series of display:inline-block; spans or divs for this purpose? Most certainly, but I will stick with the table in going forward.

The cellspacing attribute of the table element, the align attribute of the td element, and the valign attribute of the td element were all legit in HTML 4 but HTML 5 declares them non-conforming features that must not be used by authors.
• A cellspacing="10" interstitial separation can be achieved with a border-spacing:10px; styling.
• For the td element (and most other alignable elements) an align="center" horizontal centering maps to a text-align:center; styling.
• The ten table cells have a uniform shrink-to-fit height, and therefore the valign="top" attributes have no effect on the cell content (the default valignment is middle but there's just no room to move the content up or down in this case) and can be thrown out. However, if the height of the cell content area were measurably larger than the actual height of the content itself, then the valign="top" attributes could be replaced by a vertical-align:top; styling.

For their part, the San FranciscoSydney locale identifiers can be
written as normal text rather than scriptically and
bolded with a font-weight:bold; styling if desired.
Semantics-wise, are the identifiers heading-y enough to be marked up with one of the h# elements or are they really just label-like captions? I vote the latter and would code them as <label>s if we were loading the clock data into <input>s, although you may feel differently.
Our next task is to disconnect the nuts and bolts of the clock itself: all will be revealed in the following entry.


Powered by Blogger

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