reptile7's JavaScript blog
Friday, March 29, 2019
 
Times of Our World, Part 4
Blog Entry #396

Today's post will conclude our discussion of the Java/JavaScript Goodies World Clock -- Daylight Savings Time by bringing its "Daylight Savings Time" part up to speed, specifically, we will
write standard time ↔ daylight saving time functions for the dstclock locales that need them,
integrate those functions with the rest of the dstclock code, and of course
present a suitably updated dstclock demo.

Before we get rolling, let me note, in the name of completeness, that the toGMTString( ) and toUTCString( ) methods do in fact 'recognize' daylight saving time: if we're in San Francisco and it's a Sunday in July and it's 3 p.m., then the hour part of their returns will be 22. But again, these methods give a GMT/UTC time and not a local time (the Date object has other methods that give a local time, e.g., toLocaleString( )), and local times are what we want for the dstclock clock. To go from a GMT/UTC time to a corresponding local time, we need to know
(1) the number of hours that separates the GMT/UTC time and the local time - check - but also
(2) whether the local locale observes daylight saving time and if so
when the local locale's standard time ↔ daylight saving time transitions occur.
The dstclock code lets us down in the latter respect: it specifies a fixed 8-hour difference between GMT and Pacific Time, a fixed 7-hour difference between GMT and Mountain Time, etc. without taking the time of year into account.

At the time of writing - acknowledging that daylight saving time faces an uncertain future in Europe - the dstclock locales that observe daylight saving time are
San Francisco, Denver, Memphis, New York, London, Paris, and Sydney.
Our standard time ↔ daylight saving time functions for these locales will be largely based on the code samples appearing in the DST to the millisecond section of Blog Entry #103 and the Seize the day section of Blog Entry #314 and can be adapted to any other city that observes daylight saving time: Santiago, Tehran, Auckland, wherever.

American locales

California, Colorado, Tennessee, and New York all conform to the standard* U.S. daylight saving time policy whereby
standard time changes to daylight saving time at 2 a.m. local time on the second Sunday of March and
daylight saving time changes to standard time at 2 a.m. local time on the first Sunday of November
regardless of time zone. (*In this and the next two sections, I use the word "standard" in the sense of "used or accepted as normal or average".)
We can easily determine secondSun and firstSun date numbers for the transition days as follows:

var dstweek, secondSun, dstweek2, firstSun;
dstweek = new Array( );
for (var i = 8; i <= 14; i++) {
    dstweek[i] = new Date(year, 2, i);
    if (dstweek[i].getDay( ) == 0) secondSun = i; }
dstweek2 = new Array( );
for (var i = 1; i <= 7; i++) {
    dstweek2[i] = new Date(year, 10, i);
    if (dstweek2[i].getDay( ) == 0) firstSun = i; }


We next nail down the precise times at which our American locales change from standard time to daylight saving time and vice versa:

function adjust_for_us_dst(hour_offset) {
    /* dstweek, secondSun, dstweek2, firstSun determinations */
    if (secondSun < 10) "0" + secondSun;
    var HH, dstbegins, dstends;
    HH = 2 - hour_offset;
    if (HH < 10) "0" + HH;
    dstbegins = new Date(year + "-03-" + secondSun + "T" + HH + ":00:00.000Z");
    dstends = new Date(year + "-11-0" + firstSun + "T0" + (HH - 1) + ":00:00.000Z"); ...

var hour_offset;
for (var i = 0; i < localeTds.length; i++) {
    hour_offset = gmtoffsetsArray[i];
    if (0 <= i && i <= 3) hour_offset = adjust_for_us_dst(hour_offset);
    locale_hour = hour + hour_offset; ... }


For the collection of the localeTds, the creation of the gmtoffsetsArray, and the rest of the i < localeTds.length; loop, see the Code consolidation section of the previous post.

As noted earlier, American standard time ↔ daylight saving time transitions are pegged to a local time, and therefore occur at different moments in time depending on the time zone:
New York springs forward at 07:00 UTC and falls back at 06:00 UTC,
Memphis springs forward at 08:00 UTC and falls back at 07:00 UTC, etc.

We want to code the transitions in a locale-independent way so that all users everywhere (vis-à-vis users in the dstclock locales) can make use of them; creation of the dstbegins and dstends Dates is best carried out via the
new Date(dateString);
constructor syntax versus the
new Date(year, monthIndex [, day [, hours [, minutes [, seconds [, milliseconds]]]]]);
constructor syntax, which confines us to the user's local time.
(Use of the latter syntax would require us to separately cater to every time zone in the world - don't know 'bout you, but I would really rather not do that.)

The dstbegins and dstends dateStrings comply with the
YYYY-MM-DDTHH:mm:ss.sssZ
ECMAScript Date Time String Format.
Note that the MM month numbers - e.g., 03 for March - are one higher than the corresponding getMonth( ) indexes. The Zs at the end effectively place us in the UTC±0 time zone.

We lastly compare the date millisecond count with the dstbegins and dstends millisecond counts: for daylight saving time, the date count is greater than or equal to the dstbegins count AND is less than the dstends count, and if that's what we've got, then the locale hour_offset is duly pushed forward.

if (dstbegins <= date && date < dstends) hour_offset += 1;
return hour_offset; } // End of the adjust_for_us_dst( ) function


As a Date object represents a single moment in time, the date Date can be deployed as is in the preceding conditional for any user in any time zone, and does not need to be 'corrected' in any way.

FYI: The above code is also good to go for Anchorage or Fairbanks (hour_offset = -9), Alaska being the one part of the non-conterminous U.S. that observes daylight saving time.

European locales

The U.K. and France conform to the standard European daylight saving time policy whereby
standard time changes to daylight saving time at 01:00 UTC on the last Sunday of March and
daylight saving time changes to standard time at 01:00 UTC on the last Sunday of October
regardless of time zone. We can accordingly reset the hour_offsets for London and Paris during daylight saving time with:

function adjust_for_eu_dst(hour_offset) {
    var dstweek, lastSun, dstweek2, lastSun2, dstbegins, dstends;
    dstweek = new Array( );
    for (var i = 25; i <= 31; i++) {
        dstweek[i] = new Date(year, 2, i);
        if (dstweek[i].getDay( ) == 0) lastSun = i; }
    dstweek2 = new Array( );
    for (var i = 25; i <= 31; i++) {
        dstweek2[i] = new Date(year, 9, i);
        if (dstweek2[i].getDay( ) == 0) lastSun2 = i; }
    dstbegins = new Date(year + "-03-" + lastSun + "T01:00:00.000Z");
    dstends = new Date(year + "-10-" + lastSun2 + "T01:00:00.000Z");
    if (dstbegins <= date && date < dstends) hour_offset += 1;
    return hour_offset; }

// In the i < localeTds.length; loop:
if (i == 4 || i == 5) hour_offset = adjust_for_eu_dst(hour_offset);


The standard time ↔ daylight saving time transitions occur at the same moments in time for all European countries that observe daylight saving time and therefore it is unnecessary to parameterize the HH part of the dstbegins and dstends dateStrings - Lisbon, Berlin, Helsinki, we're all set.

Australian locales

New South Wales conforms to the standard Australian daylight saving time policy whereby
standard time changes to daylight saving time at 2 a.m. local time on the first Sunday of October and
daylight saving time changes to standard time at 3 a.m. local time on the first Sunday of April
regardless of time zone. We can accordingly reset the hour_offset for Sydney during daylight saving time with:

function adjust_for_oz_dst(hour_offset) {
    var dstweek, firstSun, dstweek2, firstSun2, dstbegins, dstends;
    dstweek = new Array( );
    for (var i = 1; i <= 7; i++) {
        dstweek[i] = new Date(year, 9, i);
        if (dstweek[i].getDay( ) == 0) firstSun = i; }
    dstweek2 = new Array( );
    for (var i = 1; i <= 7; i++) {
        dstweek2[i] = new Date(year, 3, i);
        if (dstweek2[i].getDay( ) == 0) firstSun2 = i; }
    dstbegins = new Date(year + "-10-0" + firstSun + "T02:00:00.000+10:00");
    dstends = new Date(year + "-04-0" + firstSun2 + "T02:00:00.000+10:00");
    if (dstbegins <= date || date < dstends) hour_offset += 1;
    return hour_offset; }

// In the i < localeTds.length; loop:
if (i == 9) hour_offset = adjust_for_oz_dst(hour_offset);


The +10:00 substring at the end of the dstbegins and dstends dateStrings effectively places us in the UTC+10 time zone; pegging dstbegins and dstends to UTC itself would in this case require us to parameterize the MM and DD parts of the dateStrings in order to accommodate years in which 1 October or 1 April falls on a Sunday.

The preceding function presumes that we are working with Sydney's hour + 10 standard-time GMT offset rather than the hour + 11 offset that appears in the original code;
we can keep the original offset
and have the function take us from daylight saving time to standard time
if we recast the function's hour_offset adjustment as:

if (dstends <= date && date < dstbegins) hour_offset -= 1;

"You titled this section Australian locales. What about Adelaide, mate?"

The standard time ↔ daylight saving time transitions for Adelaide (hour_offset = 9.5) occur 30 minutes after those for Sydney, Canberra, Melbourne, and Hobart: you can bring Adelaide into the mix via parameterizing either HH:mm part of the dstbegins and dstends dateStrings, e.g.,
year + "-10-0" + firstSun + "T02:00:00.000+" + HHmm;
where
var HHmm = hour_offset == 9.5 ? "09:30" : "10:00";.

Demo

It's time for that demo I promised you - check the page source for the full coding.

Your local time is Times listed in the following table are based on your computer's time. If your computer's clock is wrong, so is this table.
San Francisco
Denver
Memphis
New York
London
Paris
Moscow
Bangkok
Tokyo
Sydney

The next Calculators, Calendars, and Clocks item is Circle Calculator, which looks as though it's a bit of a retread
but I'll go through it and if I find anything unusual I'll report back to you.

Comments: Post a Comment

<< Home

Powered by Blogger

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