reptile7's JavaScript blog
Friday, February 29, 2008
 
Around the World in 80 Milliseconds, Act IV
Blog Entry #105

Not-on-the-hour time zones

In his discussion of the "World Clock" script of HTML Goodies' JavaScript Script Tips #87-90, Joe makes no mention of the fact that some of the world's time zones are 'not on the hour' - by this I mean that the offsets in minutes of these time zones from UTC are not multiples of 60 - which is rather odd given that one of the script's time/date display locations is New Delhi, which observes UTC+5:30 throughout the year. Wikipedia's "List of time zones" entry lists thirteen not-on-the-hour time zones: three UTC-x:30 time zones, seven UTC+x:30 time zones, and three UTC+x:45 time zones. The script's checkDateTime( ) function accommodates the various UTC±x:30 time zones via its half variable:

var half = ourDifference % 60;
ourDifference = Math.round(ourDifference / 60);
hour = hour - ourDifference;
if (half == -30 || half == 30) minute += 30;

For an initial Central Time time/date display, a UTC-9:30 user location (our Point A for the previous entry's deconstruction) gives a half value of -30, whereas any other UTC±x:30 user location will give a half value of 30. For all of the UTC±x:30 locations, the user's minute (new Date( ).getMinutes( )) value is increased by 30 so as to either
(a) push the Central Time minute further away from the user minute, or
(b) bring the Central Time minute closer to the user minute,
depending on whether the Math.round( ) command gives an apparent user/Central Time time difference (ourDifference) that is, respectively, smaller (as in the UTC-9:30 case) or larger (as in the other UTC±x:30 cases) than the actual user/Central Time time difference.

Your assignment: Work through the script from a starting location of Newfoundland, which observes UTC-3:30 during standard time, and a login time/date of your choice.

But what about the folks who live in UTC+x:45 time zones - in Nepal, on the Chatham Islands, and in the southeastern corner of Western Australia? These users will see a Central Time time that is 15 minutes earlier (in the past) than it should be. To see why, let's run through a relevant mini-deconstruction from a starting location of Nepal, which observes UTC+5:45 throughout the year.

Let's suppose that you are about to scale Mount Everest, and that shortly before you and your climbing party depart from Kathmandu and begin your ascent, you decide to do a bit of Web surfing. As a matter of course, your online session includes a visit to the Script Tips #87-90 Script demo page, which you access at exactly 6:30AM on 24 February. What happens? We turn to the end of the checkDateTime( ) DST block:

yourOffset = new Date( ).getTimezoneOffset( );
ourDifference = gmtOffset - yourOffset;

For Nepal, new Date( ).getTimezoneOffset( ) returns -345, which is assigned to yourOffset. Subsequently, yourOffset is subtracted from gmtOffset, 360 as set at the top of the script element, to give 705, which is assigned to ourDifference.

var half = ourDifference % 60;

705 % 60 gives 45, which is assigned to half.

ourDifference = Math.round(ourDifference / 60);

705 / 60 gives 11.75, which is rounded up by the Math.round( ) function to give 12, which is assigned to ourDifference.

hour = hour - ourDifference;

ourDifference is subtracted from the current Nepal hour, 6, to give -6, which is assigned to hour.

if (half == -30 || half == 30) minute += 30;

Uh-oh! The if condition returns false, so the browser moves to...

if (minute > 59) { minute -= 60; hour++; } // Condition returns false
if (minute < 0) { minute += 60; hour--; } // Condition returns false
if (hour > 23) { hour -= 24; date += 1; } // Condition returns false
if (hour < 0) { hour += 24; date -= 1; } // Condition returns true

The last of these conditionals increases hour to 18 and decrements the current Nepal date, 24, to 23. The time/date in New Orleans should therefore be 18:30 (6:30PM) on 23 February given our initial conditions, right?

Not quite. During standard time, New Orleans is 11 hours and 45 minutes 'behind' Nepal, so the time/date in New Orleans is actually 18:45 (6:45PM) on 23 February. As for the UTC±x:30 time zones, to obtain the correct New Orleans time in this case we'll need to adjust the user minute value. The short-term fix for this situation is to add the following conditional to the checkDateTime( ) function:

if (half == 45) minute += 15;

In effect, this conditional corrects for the above Math.round( ) command, which gives an apparent Nepal/New Orleans time difference (12 hours) that is larger than the actual Nepal/New Orleans time difference, by bringing the time in New Orleans 15 minutes closer to the time in Nepal.

The preceding conditional would be inadequate for 'going the other way', however. Let's suppose that the script's location button table has a Kathmandu button that triggers a function that feeds to checkDateTime( ) a gmtOffset value of -345, and that I in New Orleans am the user. Regarding the mini-deconstruction above, here are the relevant returns upon clicking the Kathmandu button at 18:45 on 23 February:
(1) yourOffset will be 360.
(2) The initial ourDifference value will be -705.
(3) half will be -45.
(4) -705 / 60 gives -11.75, which is rounded up by Math.round( ) to give -11, which is assigned to ourDifference.
(5) hour - ourDifference gives 29, which is assigned to hour.
(6) The if (hour > 23) { hour -= 24; date += 1; } conditional decreases hour to 5 and increments date to 24.

The time/date in Nepal would therefore seem to be 5:45AM on 24 February given our initial conditions; however, we know from the Nepal-to-New Orleans case that the time/date in Nepal is actually 6:30AM on 24 February. Note that in going from Nepal to New Orleans, the Math.round( ) command adds 15 minutes (0.25 hours) to the actual Nepal/New Orleans time difference, whereas in going from New Orleans to Nepal, the Math.round( ) command subtracts 45 minutes (0.75 hours) from the actual Nepal/New Orleans time difference. In going from New Orleans to Nepal, the following conditional will correct for the Math.round( ) command by pushing the Nepal minute 45 minutes 'into the future':

if (half == -45) minute += 45;

Other UTC+x:45 situations are possible. Let's suppose that you are a user in Japan, for which yourOffset would be -540 (UTC+9), and that you click the Kathmandu button described above. It is left to you to verify that in this case half will be 15 and that a

if (half == 15) minute -= 15;

conditional will be needed to correct for the Math.round( ) command.

What a nuisance all of this is, huh? Fortunately, by replacing the Math.round( ) function with the parseInt( ) function, we can deal with the UTC+x:45 time zones and also the UTC±x:30 time zones via a single minute-adjusting conditional, as follows:

yourOffset = new Date( ).getTimezoneOffset( );
ourDifference = gmtOffset - yourOffset;
var half = ourDifference % 60;
ourDifference = parseInt(ourDifference / 60);
hour = hour - ourDifference;
if (half != 0) minute -= half;
if (minute > 59) { minute -= 60; hour++; }
if (minute < 0) { minute += 60; hour--; } /* This last conditional was unnecessary in the original script, but you'll need it here. */

The above code successfully converts the user's minute to the Point B minute for half values of 30, -30, 45, -45, 15, and -15. For all of these cases, the parseInt( ) command outputs an apparent user/Point B time difference that is smaller than the actual user/Point B time difference; the if (half != 0) minute -= half conditional then pushes the Point B minute into the future or into the past, as appropriate*.

*Generally, the negative half values (-30, -45, -15) apply to not-on-the-hour time differences for which Point B is to the east of the user, requiring the Point B minute to be pushed into the future, whereas the positive half values (30, 45, 15) apply to not-on-the-hour time differences for which Point B is to the west of the user, requiring the Point B minute to be pushed into the past. Apparent exceptions to this pattern arise when we go across the International Date Line. Let's suppose that the script's location button table has a Marquesas Islands button that triggers a function that feeds to checkDateTime( ) a gmtOffset value of 570, and that you are a user in the Chatham Islands, which observes UTC+12:45 during standard time and is a Point A location 'to the west' of the Marquesas Islands. What happens, deconstruction-wise, when you click the Marquesas Islands button? I encourage you to work through this case.

Let's go to Hong Kong

We have yet to discuss the script element code associated with the location buttons on the demo page - let's do that now, shall we?

Let's say that you are a user in Philadelphia, which observes UTC-5 during standard time, and that upon arriving at the demo page you click the Hong Kong button

<input type="button" name="reset" value="Hong Kong" onclick="checkHK( );" />

at exactly 11AM on 26 February; your click triggers the checkHK( ) function.

The checkHK( ) function is one of nine location-specific functions that precede the checkDateTime( ) function in the script element and that provide to checkDateTime( ) gmtOffset and zone values appropriate for a given display location. Here it is:

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

Comments

(1) The clearTimeout(checkDateTime) command can be removed; there's no need to clear the setTimeout("checkDateTime( );", 900) timeout for the recursive function call that updates the clock display. And the clearTimeout( )/setTimeout( ) syntax is wrong anyway; if we did want to clear the timeout, we would variabilize the timeout

myTimeout = window.setTimeout("checkDateTime( );", 900);

and then plug the timeout variable into the clearTimeout( ) command:

window.clearTimeout(myTimeout);

(2) Hong Kong (indeed, all of China) observes UTC+8 throughout the year. Accordingly, -480, Hong Kong's new Date( ).getTimezoneOffset( ) return, is assigned to the variable HK at the top of the script element. Because Hong Kong does not observe daylight-saving time, HK should not be 'adjusted' by the adjust variable and should be assigned by itself to gmtOffset, i.e., gmtOffset = HK.

(3) For the demo page, Joe prefaces the zone Hong Kong string with six space characters, and not with one space character, in an attempt to approximately center it in the locationx box:

zone = "      Hong Kong";

To the best of my knowledge, a string cannot be centered in a text field via CSS.

On its last command line, checkHK( ) calls the checkDateTime( ) function. You can run through another deconstruction if you like, although it is simple enough to count forward 13 hours to determine that the time/date in Hong Kong will be 0:00 (12AM) on 27 February given our initial conditions.

The demo page's other location buttons work the same way; for example, the London button triggers a checkLD( ) function

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

that (a) picks up a globally assigned LD = 0 getTimezoneOffset( ) UTC offset, (b) adjusts the offset for DST (London does observe DST, but as noted two entries ago, adjust should be subtracted from (not added to) LD), (c) sets a new zone location, and (d) calls checkDateTime( ), which in turn acts on the checkLD( ) gmtOffset and zone values. You get the idea.

In the following entry, we'll wrap up our discussion of the Script Tips #87-90 Script with a look at some other code possibilities and also a demo featuring an expanded location button table.

reptile7

Comments: Post a Comment

<< Home

Powered by Blogger

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