サマータイム付き世界時計を汎用化してみた

サマータイム付き世界時計を汎用化してみたのメイン画像

サマータイム対応時計をもっと簡単に作成したい…。HTMLのdata属性でタイムゾーンやサマータイムの設定を記述するだけで、量産可能にしてみます。

01南半球のサマータイム

南半球のサマータイムの開始と終了日時を取得

前回、NYのサマータイムの開始と終了日を取得しました。しかし、ちょっと待ってください。南半球のサマータイムを求める場合、果たしてそのままでうまくいくでしょうか。

それでは、試しにシドニーのサマータイムを取得してみます。

Date.prototype.setTimezone = function(tz){
    var utc = new Date(this.getTime() + this.getTimezoneOffset() * 60 * 1000);
    return new Date(utc.getTime() + tz / 100 * 60 * 60 * 1000);
};
// 第n週の日曜日を取得する関数
function getTheDay(year, month, num){
    var first = new Date(year, (month - 1)),
        firstDay = first.getDay(),
        sun;
    if(firstDay == 0){
        sun = 1;
    }else{
        sun = 8 - firstDay;
    }
    sun += (num - 1)* 7;
    return sun;
}
var now = new Date(),
    dst = now.setTimezone('+1000'),
    year = dst.getFullYear(),
    start,
    end;

// サマータイム開始日
start = getTheDay(year, 10, 1); // 10月1週目の日曜日の日付を取得
start = new Date(year+'/10/'+start+' 02:00');

// サマータイム終了日
end = getTheDay(year, 4, 1); // 4月1週目の日曜日の日付を取得
end = new Date(year+'/4/'+end+' 02:00');

console.log('開始:'+ start);
console.log('終了:'+ end);
 

上記のとおり取得できましたが、ここで注意したいのは、南半球の場合、季節が逆転しているということです。シドニーの例だと、サマータイムの2019年の開始日は10月6日、2019年の終了日は4月7日で、開始と終了が逆転していますね。これは、前シーズンのサマータイムが4月7日に終了し、今シーズンのサマータイムが10月6日に始まる、ということです。

ということは、通常時間とサマータイムの条件分岐を北半球の時と変える必要があるのです!

02イスラエルのサマータイム

最後の日曜日の直前の金曜日を取得する

前回、世界のサマータイムの実施状況を調べたところ、たいていの国は日曜日に切り替えを行っていたのに対し、イスラエルだけは金曜日にサマータイムを開始していました。

まったく困ったものですね。

なお、イスラエルのサマータイムは、3月最終日曜日直前の金曜日 午前2時に開始します。それでは、さっそく開始日時を取得してみましょう。

Date.prototype.setTimezone = function(tz){
    var utc = new Date(this.getTime() + this.getTimezoneOffset() * 60 * 1000);
    return new Date(utc.getTime() + tz / 100 * 60 * 60 * 1000);
};
// 最後の日曜日を取得する関数
function getLastDay(year, month){
    var last = new Date(year,month,0),
        lastDate = last.getDate(),
        lastDay = last.getDay(),
        sun = lastDate - lastDay;
    return sun;
}

var now = new Date(),
    dst = now.setTimezone('+0200'), 
    year = dst.getFullYear(),
    start;

// サマータイム開始日
start = getLastDay(year, 3) - 2; // 最終日曜日から2を引く
start = new Date(year+'/3/'+start+' 02:00');

console.log('開始:' + start);
 

今回は、最終日曜日を起点に直前の金曜日の日付を取得するので、まず、6行目から最終日曜日を取得する関数を指定します。20行目で最終日曜日を取得し、そこから2を引くことで、直前の金曜日の日付を求めています。

これでイスラエルのサマータイム開始日を取得できました!

03量産可能なサマータイム対応時計

それでは、いよいよ、サマータイム対応時計の量産をしたいと思います!

data属性でHTMLタグにタイムゾーンやサマータイムの情報を付与する

まずは時計を出力するHTMLタグを用意します。

HTML

<!-- NYの時計 -->
<div class="clock" data-clock='{
    "tz":"-0500",
    "summer_tz":"-0400"
}'></div>

data属性の値を取得するには、下記のようにします。

javascript

$('.clock').each(function(){
    var data = $(this).data('clock'),
        tz = data.tz,
        summerTz = data.summer_tz;
    $(this).text('タイムゾーン:'+tz+',夏時間のタイムゾーン:'+summerTz);
});
 

データ属性に複数の値を指定し、そのうちのタイムゾーンの値を出力してみました。注意すべきは、data属性に値を複数設定するときは必ずシングルクォーテーションで囲んで、オブジェクトの文字列はダブルクォーテーションで囲う事です!これさえ守れば、無事、値を取得することができますね!

data属性を使って汎用的な世界時計を作る

今まで検証してきた内容を踏まえて、世界時計を汎用化してみたいと思います!

HTML

<p>上海:<span class="clock" data-clock='{"tz":"+0800"}'></span></p>
<p>ニューヨーク:
<span class="clock" data-clock='{
    "tz":"-0500",
    "summer_tz":"-0400",
    "summer_s_m":"3",
    "summer_s_w":"2",
    "summer_s_t":"02:00",
    "summer_e_m":"11",
    "summer_e_w":"1",
    "summer_e_t":"02:00"
}'></span></p>
<p>シドニー:
<span class="clock" data-clock='{
    "tz":"+1000",
    "summer_tz":"+1100",
    "summer_s_m":"10",
    "summer_s_w":"1",
    "summer_s_t":"02:00",
    "summer_e_m":"4",
    "summer_e_w":"1",
    "summer_e_t":"02:00"
}'></span></p>
<p>イスラエル:
<span class="clock" data-clock='{
    "tz":"+0200",
    "summer_tz":"+0300",
    "summer_s_m":"3",
    "summer_s_w":"fri_before_last_sun",
    "summer_s_t":"02:00",
    "summer_e_m":"10",
    "summer_e_w":"last",
    "summer_e_t":"02:00"
}'></span></p>

javascript

// タイムゾーンを設定する関数
Date.prototype.setTimezone = function(tz){
    var utc = new Date(this.getTime() + this.getTimezoneOffset() * 60 * 1000);
    return new Date(utc.getTime() + tz / 100 * 60 * 60 * 1000);
};

// 第n週の日曜日を取得する関数
function getTheDay(year, month, num){
    var first = new Date(year, (month - 1)),
        firstDay = first.getDay(),
        sun;
    if(firstDay == 0){
        sun = 1;
    }else{
        sun = 8 - firstDay;
    }
    sun += (num - 1)* 7;
    return sun;
}
// 最後の日曜日を取得する関数
function getLastDay(year, month){
    var last = new Date(year,month,0),
        lastDate = last.getDate(),
        lastDay = last.getDay(),
        sun = lastDate - lastDay;
    return sun;
}
// 時計表示の関数
function setTime(time){
    var h = ('0' + time.getHours()).slice(-2),
        m = ('0' + time.getMinutes()).slice(-2),
        s = ('0' + time.getSeconds()).slice(-2);
        msg = h + ':' + m + ':' + s;
    return msg;
}
// 開始・終了日時を求める関数
function getTheTime(year,month,week,time){
    var t;
    if(week == 'fri_before_last_sun'){
        // 最終日曜日の前の金曜日を取得
        t = getLastday(year, month) - 2;
    }else if(week == 'last'){
        // 最終日曜日を取得
        t = getLastday(year, month);
    }else{
        // 第n週の日曜日を取得
        t = getTheDay(year, month, week);
    }
    t = new Date(year+'/'+month+'/'+t+' '+time).getTime();
    return t;
}
// 世界時計の表示
$('.clock').each(function(){
    var data = $(this).data('clock'),
        Tz = data.tz,
        summerTz = data.summer_tz,
        startMonth = data.summer_s_m,
        startWeek = data.summer_s_w,
        startTime = data.summer_s_t,
        endMonth = data.summer_e_m,
        endWeek = data.summer_e_w,
        endTime = data.summer_e_t,
        $target = $(this);
    // 時計表示
    setInterval(function(){
        var now = new Date(),
            dst = now.setTimezone(Tz),
            dstTime = dst.getTime(),
            year = dst.getFullYear(),
            time = dst;
        // サマータイム実施の場合ここから
        if(summerTz != 'false'){
            //サマータイム開始・終了日時取得
            var start = getTheTime(year,startMonth,startWeek,startTime);
            var end = getTheTime(year,endMonth,endWeek,endTime);

            // 南半球と北半球で処理を分ける
            if(start < end){
                // 北半球の時刻表示
                if(dstTime >= start && dstTime < end){
                    time = now.setTimezone(summerTz);
                }
            }else{
                // 南半球の時刻表示
                if(dstTime < end || dstTime >= start){
                    time = now.setTimezone(summerTz);
                }
            }
        }
        // サマータイム実施の場合ここまで
        var msg = setTime(time);
        $target.text(msg);
    }, 1000);
});

出力結果

上海:

ニューヨーク:

シドニー:

イスラエル:

サマータイム導入している国も、していない国も、無事に時計を表示できました!

さて、詳しく見ていきたいと思います。

data-clockの中身

tz 通常時のタイムゾーン
summer_tz サマータイムのタイムゾーン
summer_s_m サマータイム開始月
summer_s_w サマータイム開始週。最終週の場合は"last"、最終日曜の直前の金曜の場合は、"fri_before_last_sun"を指定。
summer_s_t サマータイム開始時間(通常時の時間で)
summer_e_m サマータイム終了月
summer_e_w サマータイム終了週。最終週の場合は"last"を指定。
summer_e_t サマータイム終了時間(通常時の時間で)

なお、summer_~は、サマータイム未実施の場合、指定する必要はありません。

javascriptの解説

1行目~51行目までは、今までの内容をもとに、必要な処理を関数化しています。35行目~の開始・終了日時を求める関数は、data属性で指定した、サマータイムの開始週と、終了週の内容をもとに、処理を分けています。

52行目からは時計表示の処理になります。53行目、時計はclockというクラスのついた要素に出力します。54~62行目では、data属性の中身を取得しています。

64行目からは時計の表示のため、setIntervalで1秒ごとに処理しています。まず、70行目のtime = dstで、通常時の現在時刻を変数timeに代入しています。そして、72行目のif分で、サマータイム実施国かどうかを判定し、74行目と75行目で開始日時と終了日時を求めています。77行目からは、南半球と北半球とで処理を分けています。南半球の場合、サマータイム終了日より前と、サマータイム開始以降とは、条件が等しくならないので、&&ではなく||で判定しています。そして、それぞれ現在時刻をtimeに代入して、最後に92行目で$('.clock')にテキストを挿入して完了です!

いかがでしたか?これで、かんたんに世界時計を量産できそうです。ちなみに、イスラエルがイレギュラーで例外的な処理になりましたね…。その他にもイレギュラーな国はありそうなので、その場合は今回のように例外処理を書き足す必要がありそうです。

上手く動くといいなぁ~。

この記事が役に立ったらシェアしてください!

  1. 南半球のサマータイム
  2. イスラエルのサマータイム
  3. 量産可能なサマータイム対応時計