2011年4月24日日曜日

振替休日を算出するJavaScriptカレンダー

YUIのJavaScriptカレンダーを日本語化&ポップアップ対応&祝日も表示 - Yahho Calendar [ゼロと無限の間に]というサイトでGoogleカレンダーの祝日情報を取得して、カレンダーポップアップを表示するサンプルが公開されていたのですが、何故かGoogleカレンダーの祝日情報は元旦の振替休日(2012年)しか登録されていないので、振替休日をJavaScript内で算出するように修正してみました。

ここをクリックするとカレンダーが表示されます。
月の祝日を (何も入力しないと今年の祝日を表示します。)

振替休日と国民の休日の算出方法でお悩みの方がいましたら、続きをご覧ください。

  1. 振替休日の算出
    Wikipediaの説明によると
    「国民の祝日」が日曜日に当たるときは、その日後においてその日に最も近い「国民の祝日」でない日を休日とする
    と決まっています。
    なので、

    (1) 祝日が日曜日だったら、祝日を1日ずらす
    (2) ずらした祝日が他の祝日と重なった場合、祝日を1日ずらす

    を(1)、(2)に該当しない日にちになるまで繰り返すというような処理で算出できるようになります。
    それを関数にしたのがこちらになります。
    /**
     * 振替休日を算出する
     * @param Date     ymdDate 休日
     * @param entries  休日リスト
     * @param next     指定した休日の次の休日の引数
     */
    GCalHolidays._compensating_holiday = function(ymdDate, entries, next) {
     
     var nextYmd = null;
     var _ymdDate = new Date();
     _ymdDate.setTime(ymdDate.getTime());
    
     // 
     if (entries.length > next) {
      nextYmd = new Date(entries[next].gd$when[0].startTime.split("T")[0].replace("-", "/"));
     }
     
     // 日曜日の場合は、振替休日にする
     if (_ymdDate.getDay() == 0) {
      _ymdDate.setTime(_ymdDate.getTime() + (1000 * 60 * 60 * 24));
      // 再帰して、振替休日にした日が休日の場合、再度休日を算出する
      _ymdDate = GCalHolidays._compensating_holiday(_ymdDate, entries, next);
     }
     // 振替休日が祝日だった場合
     else if (nextYmd != null
          && _ymdDate.getTime() == nextYmd.getTime()) {
      // 何故かたまにちゃんと振替休日が登録されている。
      if (entries[next].title.$t == "振替休日") {
       return null;
      }
      else {
       _ymdDate.setTime(ymdDate.getTime() + (1000 * 60 * 60 * 24));
       // 再帰して、振替休日にした日が休日の場合、再度休日を算出する
       _ymdDate = GCalHolidays._compensating_holiday(_ymdDate, entries, (next + 1));
      }
     }
     
     return _ymdDate;
    };
    
  2. 国民の休日の算出
    前後を祝日にはさまれた休日でない日は休日が適用され「国民の休日」となります。
    過去には2009年の9月に適用されました。
    これは、単純に連続する2つの休日の間隔が2日の場合、間の一日を休日にすればよいことになります。
    これをコードにすると下記のようになります。
    // 国民の休日(tmpDate = 前の祝日 ymdDate = 後の祝日)
    if (tmpDate != null
        && (ymdDate.getTime() - tmpDate.getTime()) == (2 * 1000 * 60 * 60 * 24)) {
      counter += 1;
      var holidayDate = new Date();
      holidayDate.setTime(tmpDate.getTime() + (1000 * 60 * 60 * 24));
    }
    
最後に「gcalendar-holidays.js」の修正分をまとめて公開しておきます。
/**
 * 振替休日を算出する
 * @param Date     ymdDate 休日
 * @param entries  休日リスト
 * @param next     指定した休日の次の休日の引数
 */
GCalHolidays._compensating_holiday = function(ymdDate, entries, next) {
  
  var nextYmd = null;
  var _ymdDate = new Date();
  _ymdDate.setTime(ymdDate.getTime());

  // 
  if (entries.length > next) {
    nextYmd = new Date(entries[next].gd$when[0].startTime.split("T")[0].replace("-", "/"));
  }
  
  // 日曜日の場合は、振替休日にする
  if (_ymdDate.getDay() == 0) {
    _ymdDate.setTime(_ymdDate.getTime() + (1000 * 60 * 60 * 24));
    // 再帰して、振替休日にした日が休日の場合、再度休日を算出する
    _ymdDate = GCalHolidays._compensating_holiday(_ymdDate, entries, next);
  }
  // 振替休日が祝日だった場合
  else if (nextYmd != null
            && _ymdDate.getTime() == nextYmd.getTime()) {
    // 何故かたまにちゃんと振替休日が登録されている。
    if (entries[next].title.$t == "振替休日") {
      return null;
    }
    else {
      _ymdDate.setTime(ymdDate.getTime() + (1000 * 60 * 60 * 24));
      // 再帰して、振替休日にした日が休日の場合、再度休日を算出する
      _ymdDate = GCalHolidays._compensating_holiday(_ymdDate, entries, (next + 1));
    }
  }
  
  return _ymdDate;
};
/**
 *  JSONPで取得したデータから日付情報を取り出す
 *  @param  Array   entries スケジュール
 *  @return Array   日付情報(year, month, date, title)
 */
GCalHolidays._entries2days = function(entries) {
    var days = [];
    var counter = 0;
    var tmpDate = null;
    var nextIsShuubun = false;

    var addExtraDays = function () {
        // 振替休日の算出
        ymdDate = GCalHolidays._compensating_holiday(baseDate, entries, (i + 1));
        ymdDate = (ymdDate == null) ? baseDate : ymdDate;
        // 振替休日を追加する
        if(ymdDate.getTime() != baseDate.getTime()) {
          counter += 1;
          days[(i + counter)] = {year: ymdDate.getFullYear(), month: ymdDate.getMonth() + 1, date: ymdDate.getDate(), title: "振替休日"};
        }
        // 国民の休日
        if (tmpDate != null
            && (ymdDate.getTime() - tmpDate.getTime()) == (2 * 1000 * 60 * 60 * 24)) {
          counter += 1;
          var holidayDate = new Date();
          holidayDate.setTime(tmpDate.getTime() + (1000 * 60 * 60 * 24));
          days[(i + counter)] = {year: holidayDate.getFullYear(), month: holidayDate.getMonth() + 1, date: holidayDate.getDate(), title: "国民の休日"};
        }
    }
    
    var addShuubun = function() {
      //年月日は使いやすいように数値にする
      days[(i + counter)] = {year:baseDate.getFullYear(), month:9, date:23, title:"秋分の日"};
      var _baseDate = new Date();
      _baseDate.setTime(baseDate.getTime());
      baseDate.setMonth(8);
      baseDate.setDate(23);
      // 振替休日の算出
      ymdDate = GCalHolidays._compensating_holiday(baseDate, entries, (i + 1));
      ymdDate = (ymdDate == null) ? baseDate : ymdDate;
      // 振替休日、国民の休日などを追加する
      addExtraDays();
      baseDate.setTime(_baseDate.getTime());
      counter += 1;
    }
    
    if (!entries) {
        return days;
    }

    //日付順にソート
    entries.sort(function(a, b) {
        return (a.gd$when[0].startTime > b.gd$when[0].startTime) ? 1 : -1;
    });

    //日付順にソート
    var ymdDate = null;
    //シンプルな器に移す
    for (var i = 0, len = entries.length; i < len; i++) {
        var title = entries[i].title.$t;
        // 何故か2009年に秋分の日が抜けているので対応。
        if (nextIsShuubun
            && title != "秋分の日") {
              alert(title);
          addShuubun();
        }
        var baseDate = new Date(entries[i].gd$when[0].startTime.split("T")[0].replace("-", "/"));
        //年月日は使いやすいように数値にする
        days[(i + counter)] = { year: baseDate.getFullYear(), month: baseDate.getMonth() + 1, date: baseDate.getDate(), title: title };
        // 振替休日、国民の休日などを追加する
        addExtraDays();
        tmpDate = ymdDate;
        nextIsShuubun = (title == "敬老の日");
    }
    
    if (nextIsShuubun == true) {
      addShuubun();
    }
    
    //日付順にソート
    days.sort(function(a, b) {
        return (new Date(a.year + "/" + a.month + "/" + a.date).getTime() > new Date(b.year + "/" + b.month + "/" + b.date).getTime()) ? 1 : -1;
    });

    return days;
};

0 件のコメント:

コメントを投稿