JavaScriptで、祝日を調べる(取得する)(明治からも対応)

ちょい書きかけ


う〜〜〜ん。明治とか大正の時代の休日も今どきとして必要なのか?
今さらながら、作ってから気づく。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <style>
  
  </style>
</head>

<body>
<h3>六曜について</h3>

<pre>
n = (month + day) % 6;
ary = ['大安', '赤口', '先勝', '友引', '先負', '仏滅'];
rst = ary[n];

</pre>
<script>

{
  const
    CURRENT_DATE = new Date,
    CURRENT_YEAR = CURRENT_DATE.getFullYear (),
    TITLE_FURIKAE = '(振替休日)',
    TITLE_KOKUMIN = '(国民の休日)',
    
    SHUKUJITU_FUNC = [
      // 国民の祝日 「祝日法」 1948-7-20施行
      ganjitu, // 元日
      seijin_no_hi, // 成人の日
      kenkokukinen_no_hi, // 建国記念の日
      shunbun_no_hi, // 春分の日
      shouwa_tennou, // 昭和の日、みどりの日、昭和天皇誕生日
      kenpou_kinenbi, // 憲法記念日
      midori_no_hi, // みどりの日
      kodomo_no_hi, // こどもの日
      umi_no_hi, // 海の日
      yama_no_hi, // 山の日
      keirou_no_hi, // 敬老の日
      shuubun_no_hi, // 秋分の日
      taiiku_no_hi, // 体育の日
      bunka_no_hi, // 文化の日
      kinroukansha_no_hi, // 勤労感謝の日
      heisei_tennou, //(平成)天皇誕生日

      // 祝祭日     「年中祭日祝日ノ休暇日ヲ定ム」 1873-10-14施行
      sihousetu, //四方節 -	
      sinnenenkai, //新年宴会
      kigensetu, //紀元節
      tentyousetu, //天長節
      tentyousetu_shukujitu, //天長節祝日
      meijisetu, //明治節
      
      gensisai, //元始祭
      koumeitennousai, //孝明天皇祭
      shunkikoureisai, //春季皇霊祭
      jinmutennousai, //神武天皇祭
      meijitennousai, //明治天皇祭
      kannamesai, //神嘗祭
      shuukikoureisai, //秋季皇霊祭
      kannamesai2, //神嘗祭
      niinamesai, //新嘗祭
      taishoutennousai //大正天皇祭
    ];
  
  //_________________________________________________________________
  
  //指定の年月のn週のw曜日の日にちを返す
  function getWN (year, month, week_num, day_num) {
    let
      d = new Date (year, month, 1),
      td = day_num - d.getDay () + 1;
    if (td <= 0)
      td += 7;
    td += 7 * (week_num - 1);

    return td;
  }
  
  //日付順にソート用
  function sortByDate (a, b) {
    return a.date > b.date;
  }
  
  //次の日にする
  function nextDate (d = new Date, n = 1) {
    d.setDate (d.getDate () + n);
    return d;
  }
  
  //日付が等しいか
  function isSameDate (a, b) {
    let rst =
      (!a) || (!b) ||
      (a.getFullYear () !== b.getFullYear ()) ||
      (a.getMonth ()    !== b.getMonth ()) ||
      (a.getDate ()     !== b.getDate ());
    return !rst;
  }
  
  //年月日の属性を付加する
  function addYMDmethod (list) {
    for (let rec of list) {
      let d = rec.date;
      rec.year = d.getFullYear ();
      rec.month = d.getMonth () + 1;
      rec.day = d.getDate ();
    }
    return list;
  }
  
  
  //_________________________________________________________________
  
  //元日
  function ganjitu (y) {
    return 1948 < y ? {name: '元日', date: new Date (y, 0, 1)}: null;
  }
  //成人の日
  function seijin_no_hi (y) {
   return 1948 < y ? {name: '成人の日', date: new Date (y, 0, y < 2000 ? 15: getWN (y, 0, 2, 1))}: null;
  }
  //建国記念の日
  function kenkokukinen_no_hi (y) {
    return 1948 < y ? { name: '建国記念の日', date: new Date (y, 1, 11)}: null;
  }
  //春分の日
  function shunbun_no_hi (y) {
    if (y <= 1948) return null;
    let d;
    
    if (y < 1980)
      d = parseInt(20.8357 + 0.242194 * ( y - 1980), 10) - parseInt((y - 1983) / 4, 10);
    else if (y < 2100)
      d = parseInt(20.8431 + 0.242194 * ( y - 1980), 10) - parseInt((y - 1980) / 4, 10);
    else if (y < 2150)
      d = parseInt(20.8510 + 0.242194 * ( y - 1980), 10) - parseInt((y - 1980) / 4, 10);
    else
      throw new Error ('範囲外です' + y);
    
    return { name: '春分の日', date: new Date (y, 2, d) };
  }
  //天皇誕生日, みどりの日, 昭和の日
  function shouwa_tennou (y) {
    return 1948 < y ? {name: y < 1989 ? '天皇誕生日': y < 2007 ? 'みどりの日': '昭和の日', date: new Date (y, 3, 29)}: null;
  }
  //憲法記念日
  function kenpou_kinenbi (y) {
    return 1948 < y ? { name: '憲法記念日', date: new Date (y, 4, 3)}: null;
  }
  //みどりの日
  function midori_no_hi (y) {
    return 2006 < y ? { name: 'みどりの日', date: new Date (y, 4, 4)}: null;
  }
  //こどもの日
  function kodomo_no_hi (y) {
    return 1949 < y ? { name: 'こどもの日', date: new Date (y, 4, 5)}: null;
  }
  //海の日
  function umi_no_hi (y) {
    return 1995 < y ? { name: '海の日', date: new Date (y, 6, 2002 < y ? getWN (y, 6, 3, 1): 20 )}: null;
  }
  //山の日
  function yama_no_hi (y) {
    return 2015 < y ? {name: '山の日', date: new Date (y, 7, 11)}: null;
  }
  //敬老の日
  function keirou_no_hi (y) {
    return 1965 < y ? {name: '敬老の日', date: new Date (y, 8, y < 2003 ? 15: getWN (y, 8, 3, 1))}: null;
  }
  //秋分の日
  function shuubun_no_hi (y) {
    if (y < 1948) return null; //法の施行後

    let d;
    if (y < 1980)
      d = parseInt(23.2588 + 0.242194 * (y - 1980), 10) - parseInt((y - 1983) / 4, 10);
    else if (y < 2100)
      d = parseInt(23.2488 + 0.242194 * (y - 1980), 10) - parseInt((y - 1980) / 4, 10);
    else if (y < 2150)
      d = parseInt(24.2488 + 0.242194 * (y - 1980), 10) - parseInt((y - 1980) / 4, 10);
    else
      throw new Error ('範囲外です');

    return { name: '秋分の日', date: new Date (y, 8, d)};
  }
  //体育の日
  function taiiku_no_hi (y) {
    return 1965 < y ? {name: '体育の日', date: new Date (y, 9, y < 2000 ? 10: getWN (y, 9, 2, 1))}: null;
  }
  //文化の日
  function bunka_no_hi (y) {
    return 1947 < y ? {name: '文化の日', date:  new Date (y, 10, 3)}: null;
  }
  //勤労感謝の日
  function kinroukansha_no_hi (y) {
    return 1947 < y ? {name: '勤労感謝の日', date:new Date (y, 10, 23)}: null;
  }
  //天皇誕生日
  function heisei_tennou (y) {
    return 1988 < y ? y < 2019 ? { name: '天皇誕生日', date: new Date (y, 11, 23)}: null: null;
  }
  //振替休日
  function furikaeKyuujitu (list = [ ]) {
    let
      furikae = [ ],
      prev = null;
      begin = +new Date (1973, 3, 12), //.getTime ();
      end   = +new Date (2007, 0, 1); //.getTime () --> 2006/12/31:23:59:59:999?

    list.forEach (obj => {
      if (+obj.date < begin) return;//1973以前は振替休日がない
      
      let date = new Date (obj.date);
      
      //2006/12/31以前:国民の祝日が日曜のとき次の日は振替休日
      if (+date < end) {
        if (0 === date.getDay ())//日曜
          furikae.push ({name: TITLE_FURIKAE, date: nextDate (date)});
      }
      //2007以降、国民の祝日が連続しており、その中に日曜が含まれるとき次の平日は振替休日
      else {
        if (prev) {
          if (isSameDate (prev, date))
            nextDate (prev);
          else {
            furikae.push ({name: TITLE_FURIKAE, date: prev});
            prev = null;
          }
        }
        if (0 === date.getDay ())
          prev = nextDate (date);
      }  
    });
    
    return furikae;
  }
  
  //国民の休日
  function kokuminKyuujitu (list = [ ]) {
    if (! list.length)
      throw new Error ('');
      
    let
      kyuujitu = [ ],
      y = list[0].date.getFullYear ();//祝日の配列の最初のDATEを基準とする
      prv = null,
      begin    = new Date (1985, 11, 27);

    //1986-2006の5月4日の特殊処理
    if (1986 <= y && y <= 2006)
      kyuujitu.push ({ name: TITLE_KOKUMIN, date: new Date (y, 4, 4)});

    //1985/12/27以降
    for (let cur of list) {
      let date = new Date (cur.date);
      if (begin <= +date) {
        if (isSameDate (prv, date)) {
          if (1 < nextDate (prv, -1).getDay ())
            kyuujitu.push ({name: TITLE_KOKUMIN, date: prv});
          prv = nextDate (date, 2);
        }
      }
    }
    return kyuujitu;
  }


  //_________________________________________________________________

  // 国民の祝日 「祝日法」 1948-7-20施行
  //四方節
  function sihousetu (y) {//法令で定められてはいない
    return 1873 < y ? y <= 1948 ? { name: '四方節', date: new Date (y, 0, 1) }: null: null;
  }
  //新年宴会
  function sinnenenkai (y) {
    return 1873 < y ? { name: '新年宴会', date: new Date (y, 0, 5) }: null;
  }
  //紀元節
  function kigensetu (y) {
    return 1873 < y ? { name: '紀元節', date: new Date (y, 1, 11) }: null;
  }
  //天長節
  function tentyousetu (y) {
    let date;
    switch (true) {
    case y < 1874 : case y === 1912 : case 1948 < y : return null; break;
    case y < 1912 : date = new Date (y, 10, 3); break;
    case y < 1927 : date = new Date (y, 7, 31); break;
    case y < 1949 : date = new Date (y, 3, 29); break;
    default : throw new Error ('year = ' + y); break;
    }
    
    return { name: '天長節', date };
  }
  //天長節祝日
  function tentyousetu_shukujitu (y) {
    return 1913 <= y ? y <= 1926 ? { name: '天長節祝日', date: new Date (y, 9, 31) }: null: null;
  }
  //明治節
  function meijisetu (y) {
    return 1927 <= y ? y < 1948 ? { name: '明治節', date: new Date (y, 10, 3) }: null: null;
  }
  
  //元始祭
  function gensisai (y) {
    return 1873 < y ? y <= 1948 ? { name: '元始祭', date: new Date (y, 0, 3) }: null: null;
  }
  //孝明天皇祭
  function koumeitennousai (y) {
    return 1873 < y ? y <= 1912 ? { name: '孝明天皇祭', date: new Date (y, 0, 30) }: null: null;
  }
  //春季皇霊祭
  function shunkikoureisai (y) {
    return 1879 <= y ? y <= 1948 ? { name: '春季皇霊祭', date: new Date (y, 2, 21) }: null: null;
  }
  //神武天皇祭
  function jinmutennousai (y) {
    return 1873 < y ? y <= 1948 ? { name: '神武天皇祭', date: new Date (y, 3, 3) }: null: null;
  }
  //明治天皇祭
  function meijitennousai (y) {
    return 1913 <= y ? y <= 1926 ? { name: '明治天皇祭', date: new Date (y, 6, 30) }: null: null;
  }
  //神嘗祭
  function kannamesai (y) {
    return 1873 <= y ? y <= 1878 ? { name: '神嘗祭', date: new Date (y, 8, 17) }: null: null;
  }
  //秋季皇霊祭
  function shuukikoureisai (y) {
    return 1878 <= y ? y < 1948 ? { name: '秋季皇霊祭', date: new Date (y, 8, 23) }: null: null;
  }
  //神嘗祭(2)
  function kannamesai2 (y) {
    return 1879 <= y ? y < 1948 ? { name: '神嘗祭', date: new Date (y, 9, 17) }: null: null;
  }
  //新嘗祭
  function niinamesai (y) {
    return 1873 <= y ? y < 1948 ? { name: '新嘗祭', date: new Date (y, 10, 23) }: null: null;
  }
  //大正天皇祭
  function taishoutennousai (y) {
    return 1927 <= y ? y < 1948 ? { name: '大正天皇祭', date: new Date (y, 11, 25) }: null: null;
  } 


  //_________________________________________________________________
 
  //皇室慶弔行事
  function kousitu_gyouji (y) {
    switch (y) {
      case 1915 :
        return [
          {name: '即位の礼',  date: new Date (y, 10, 10)},
          {name: '大嘗祭',    date: new Date (y, 10, 14)},
          {name: '大饗第1日', date: new Date (y, 10, 16)}
        ];
        break;
    
      case 1928 :
        return [
          {name: '即位の礼',  date: new Date (y, 10, 10)},
          {name: '大嘗祭',    date: new Date (y, 10, 14)},
          {name: '大饗第1日', date: new Date (y, 10, 16)}
        ];
        break;
      
    case 1959 :
      return [{name: '皇太子・明仁親王の結婚の儀', date: new Date (y, 3, 10)}];
      break;

    case 1989 :
      return [{name: '昭和天皇の大喪の礼', date: new Date (y, 1, 24)}];
      break;

    case 1990 :
      return [{name: '即位の礼正殿の儀', date: new Date (y, 10, 12)}];
      break;
    
    case 1993 :
      return [{name: '皇太子・皇太子徳仁親王の結婚の儀', date: new Date (y, 5, 9)}];
      break;
    
    case 2019 :
      return [
        {name: '天皇の即位', date: new Date (y, 4, 1)},
        {name: '即位の礼正殿の儀', date: new Date (y, 9, 22)}
      ];
      break;
    
    default :
      return [ ];
    }
  }

  
  //_________________________________________________________________
 
   class Shukujitu {
    constructor (year = CURRENT_YEAR) {
      this.year = year;
      this.holidays = Shukujitu.getHolidayList ();
    }
    
    
  //_____
    // arg0 = DateObject or Year(integer);
    //arg1 = 0〜11は月数、省略or -1 は、指定年のすべての祝日を返す
    static getHolidayList (date = CURRENT_DATE, month = -1) {
      if (! date instanceof Date) {
        if (Number.isInteger (date))
          date = new Date (date, 0, 1);
        else
          throw new Error ('年数の値が範囲外');
      }
      if (month < -1 || 12 < month)
        throw new Error ('月数の値が範囲外');
      
      let
        year = typeof date === 'object' ? date.getFullYear (): date,
        holidays = [ ], furikae = [ ], kokumin = [ ], kousitu = [ ];
      
      SHUKUJITU_FUNC.forEach (cbfunc => {
        let rst = cbfunc (year);
        if (rst)
          holidays.push (rst);
      });
      
      holidays = holidays.sort (sortByDate);
      furikae  = furikaeKyuujitu (holidays);
      kokumin  = kokuminKyuujitu (holidays);
      kousitu  = kousitu_gyouji (year);//皇室行事は祝日と重ならないのでこのまま混合する
      
      holidays = addYMDmethod (holidays.concat (furikae, kokumin, kousitu).sort (sortByDate));
      if (-1 < month)
        holidays = holidays.filter (dt => month === dt.date.getMonth ());
      return holidays;
    }
    
    
  }
  
  //_____
  
  this.Shukujitu = Shukujitu;
}


//________________________________________________________

//実行サンプル

Shukujitu.getHolidayList (2018, 10).forEach (a =>
  a ? console.log (a.date.getFullYear(), a.date.getMonth () + 1, a.date.getDate (), a.name): null);



</script>