六曜のカレンダーを表示する(書きかけ)
六曜の結果なのだが、今月は正確に表示されないらしい。
見直す羽目になる。
http://jsdo.it/babu_baboo/EP6b
<!DOCTYPE html> <meta charset="utf-8"> <title>六曜を計算</title> <style> caption { font-size: x-large; text-align: center; } caption label { margin: 0 1em } th { height: 2em; background: #aea; } td { height: 3em; width: 3em; vertical-align: top; background: #ffd; } td:nth-of-type(1) { background: #fdd; color: #c00; } td:nth-of-type(7) { background: #eef; color: #008; } td.prev, td.next { color: #ccc; } </style> <body> <table border="1"> <caption> <input type="button" value="←" id ="PREV_MONTH"> <label></label> <input type="button" value="→" id ="NEXT_MONTH"> </caption> <thead><tr><th>日<th>月<th>火<th>水<th>木<th>金<th>土 <tbody> </table> <script> "use strict" // 六曜を返す { const abs = Math.abs, deg = Math.PI / 180, int = num => num > 0 ? Math.floor (num): Math.ceil (num), sum = (a, b) => a + b, COS = t => Math.cos (NORMALIZATION_ANGLE (t) * deg), TMZ = 9 / 24, TM_LIMIT = 1 / 86400, ROKUYOU_STR_JP = ['大安', '赤口', '先勝', '友引', '先負', '仏滅'], SEKKI24_STR_JP = [ '春分', '清明', '穀雨', '立夏', '小満', '芒種', '夏至', '小暑', '大暑', '立秋', '処暑', '白露', '秋分', '寒露', '霜降', '立冬', '小雪', '大雪', '冬至', '小寒', '大寒', '立春', '雨水', '啓蟄' ], LONGITUDE_SUN_PARAMETER = [ [ 0.0004, 31557, 161], [ 0.0004, 29930, 48], [ 0.0005, 2281, 221], [ 0.0005, 155, 118], [ 0.0006, 33718, 316], [ 0.0007, 9038, 64], [ 0.0007, 3035, 110], [ 0.0007, 65929, 45], [ 0.0013, 22519, 352], [ 0.0015, 45038, 254], [ 0.0018, 445267, 208], [ 0.0018, 19, 159], [ 0.0020, 32964, 158], [ 0.0200, 71998, 265], [-0.0048, 35999.05, 267.52], [1.9147, 35999.05, 267.52] ], LONGITUDE_MOON_PARAMETER = [ [0.0003, 2322131, 191], [0.0003, 4067, 70], [0.0003, 549197, 220], [0.0003, 1808933, 58], [0.0003, 349472, 337], [0.0003, 381404, 354], [0.0003, 958465, 340], [0.0004, 12006, 187], [0.0004, 39871, 223], [0.0005, 509131, 242], [0.0005, 1745069, 24], [0.0005, 1908795, 90], [0.0006, 2258267, 156], [0.0006, 111869, 38], [0.0007, 27864, 127], [0.0007, 485333, 186], [0.0007, 405201, 50], [0.0007, 790672, 114], [0.0008, 1403732, 98], [0.0009, 858602, 129], [0.0011, 1920802, 186], [0.0012, 1267871, 249], [0.0016, 1856938, 152], [0.0018, 401329, 274], [0.0021, 341337, 16], [0.0021, 71998, 85], [0.0021, 990397, 357], [0.0022, 818536, 151], [0.0023, 922466, 163], [0.0024, 99863, 122], [0.0026, 1379739, 17], [0.0027, 918399, 182], [0.0028, 1934, 145], [0.0037, 541062, 259], [0.0038, 1781068, 21], [0.0040, 133, 29], [0.0040, 1844932, 56], [0.0040, 1331734, 283], [0.0050, 481266, 205], [0.0052, 31932, 107], [0.0068, 926533, 323], [0.0079, 449334, 188], [0.0085, 826671, 111], [0.0100, 1431597, 315], [0.0107, 1303870, 246], [0.0110, 489205, 142], [0.0125, 1443603, 52], [0.0154, 75870, 41], [0.0304, 513197.9, 222.5], [0.0347, 445267.1, 27.9], [0.0409, 441199.8, 47.4], [0.0458, 854535.2, 148.2], [0.0533, 1367733.1, 280.7], [0.0571, 377336.3, 13.2], [0.0588, 63863.5, 124.2], [0.1144, 966404.0, 276.5], [0.1851, 35999.05, 87.53], [0.2136, 954397.74, 179.93], [0.6583, 890534.22, 145.7 ], [1.2740, 413335.35, 10.74], [6.2888, 477198.868, 44.963] ], //_________ //六曜を数値で返す get_rokuyou = (date) => { let [,, m, d] = calc_kyureki (date); return (m + d) % 6; //0:大安 1:赤口 2:先勝 3:友引 4:先負 5:仏滅 }, //_________ //六曜を漢字文字列で返す get_rokuyou_kanji = (date) => { return ROKUYOU_STR_JP[get_rokuyou (date)]; }, //_________ //新暦に対応する、旧暦を求める。戻値: [0:旧暦年, 1: 0|1 (平月/閏月), 2:旧暦月, 3:旧暦日] calc_kyureki = (date) => { let kyureki = [ ], saku = [ ], chu = [ ], /*---------------------------------------------------------------------- * 計算対象の直前にあたる二分二至(夏至・冬至・春分・秋分)の時刻を求める * chu[0,0]:二分二至の時刻 chu[0,1]:その時の太陽黄経(Sun yellow through) *---------------------------------------------------------------------*/ tm0 = YMDT2JD (date), int_tm0 = int (tm0); // 中気の時刻を計算(4回計算する) chu[i,0]:中気の時刻 chu[i,1]:太陽黄経 let [time] = chu[0] = before_nibun (tm0); for (let i = 1; i < 4; i++) [time] = chu[i] = calc_chu (time + 32); //計算対象の直前にあたる二分二至の直前の朔の時刻を求める let tm = saku[0] = calc_saku (chu[0][0]); for (let i = 1; i < 5; i++) { let s = calc_saku (tm + 30); tm = saku[i] = (abs (int (tm) - int (s)) <= 26) ? calc_saku (tm + 35): s; //前と同じ時刻を計算した場合(両者の差が26日以内)には、初期値を+33日にして再実行させる。 } /*---------------------------------------------------------------------- # saku[1]が二分二至の時刻以前になってしまった場合には、朔をさかのぼり過ぎ # たと考えて、朔の時刻を繰り下げて修正する。 # その際、計算もれ(saku[4])になっている部分を補うため、朔の時刻を計算 # する。(近日点通過の近辺で朔があると起こる事があるようだ...?) #---------------------------------------------------------------------*/ if (int (saku[1]) <= int (chu[0][0])) { saku.shift (); saku.push (calc_saku (saku[3] + 35));//配列の先頭を削除し最後に追加 } /*---------------------------------------------------------------------- # saku[0]が二分二至の時刻以後になってしまった場合には、朔をさかのぼり足 # りないと見て、朔の時刻を繰り上げて修正する。 # その際、計算もれ(saku[0])になっている部分を補うため、朔の時刻を計算 # する。(春分点の近辺で朔があると起こる事があるようだ...?) #---------------------------------------------------------------------*/ else if (int (saku[0]) > int (chu[0][0])) { saku.pop (); saku.unshift (calc_saku (saku[0] - 27)); //配列の最後を削除し先頭に追加 } /*---------------------------------------------------------------------- # 閏月検索Flagセット # (節月で4ヶ月の間に朔が5回あると、閏月がある可能性がある。) # lap=0:平月 lap=1:閏月 #---------------------------------------------------------------------*/ let lap = (int (saku[4]) <= int (chu[3][0])) ? 1: 0, /*---------------------------------------------------------------------- * 朔日行列の作成 * m[i,0] ... 月名(1:正月 2:2月 3:3月 ....) * m[i,1] ... 閏フラグ(0:平月 1:閏月) * m[i,2] ... 朔日のjd *---------------------------------------------------------------------*/ bm = int (chu[0][1] / 30) + 2, m = [[bm, 0, int (saku[0])]]; for (let i = 1; i < 5; i++) { if (lap == 1 && i != 1) { if (int (chu[i - 1][0]) <= int (saku[i - 1]) || int (chu[i - 1][0]) >= int (saku[i])) { m[i - 1] = [m[i - 2][0], 1, int (saku[i - 1])]; lap = 0; } } [bm] = m[i] = [bm % 12 + 1, 0, int (saku[i])]; //bm = m[i][0]; } // 朔日行列から旧暦を求める。 let M; for (let cnt = 0; cnt < 5; cnt += 1) { M = m[cnt]; let m2 = M[2]; if (int_tm0 < m2) { M = m[cnt - 1]; break; } if (int_tm0 == m2) { break; } } if (! M) throw new Error (); let [qYear, month] = JD2YMDT (tm0), [qMonth, qMuru, sakuDay] = M, qDay = int (tm0) - int (sakuDay) + 1; //旧暦年の計算(旧暦月が10以上でかつ新暦月より大きい場合には、まだ年を越していないはず...) if (qMonth > 9 && qMonth > month) qYear -= 1; return [qYear, qMuru, qMonth, qDay]; }, //_________ // 中気の時刻を求める calc_chu = tm => { // tm = ユリウス日 let rm_sun = getSunYellowThrough (tm);//λsun 黄経を計算 return correctionTime (tm, rm_sun, 30 * int (rm_sun / 30));//中気の黄経 λsun0 }, //_________ // 直前の二分二至の時刻を求める before_nibun = tm => { //ユリウス日 let rm_sun = getSunYellowThrough (tm);//λsun 黄経を計算 return correctionTime (tm, rm_sun, 90 * int (rm_sun / 90)); }, //_________ //時間補正 correctionTime = (tm, rm_sun, rm_sun0) => { let tm1 = int (tm), //時刻引数を分解する tm2 = tm - tm1 - TMZ, //JST ==> DT (補正時刻=0.0sec と仮定して計算) dt, dt1, dt2; do { [dt, dt1, dt2] = angleCorrection (rm_sun - rm_sun0);//黄経から時刻の補正 tm1 -= dt1; tm2 -= dt2; if (tm2 < 0) { tm2 += 1; tm1 -= 1; } rm_sun = getLongitudeSun (tm1, tm2);//λsun を計算 } while (dt > TM_LIMIT); // 直前の二分二至の時刻を計算する(誤差が±1.0 sec以内になったら打ち切る。) return [tm1 + tm2 + TMZ, rm_sun0]; // nibun[0:二分二至の時刻, 1:その時の黄経] }, //角度の補正 angleCorrection = angle => { angle += (angle > 180) ? -360: (angle < -180) ? 360: 0;//角度の範囲を補正(±180°) let t0 = angle * 365.2 / 360; return [abs (t0), int (t0), t0 % 1]; }, //λsun を計算 getLongitudeSun = (tm1, tm2) => LONGITUDE_SUN ((tm1 + tm2 + 0.5 - 2451545) / 36525), //_________ // 与えられた時刻の直近の朔の時刻(JST)計算 戻り値:ユリウス日で表し、時分秒は日の小数 calc_saku = (tm) => {//ユリウス日 let lc = 1, //ループカウンタのセット tm1 = int (tm), //時刻引数を分解する tm2 = tm - tm1 - TMZ, //JST ==> DT (補正時刻=0.0sec と仮定して計算) t0; //朔の時刻を計算する(誤差が±1.0 sec以内になったら打ち切る) do { // ループ回数が15回になったら、初期値 tm を tm-26 とする。 if (lc === 15) { tm1 = int (tm - 26); tm2 = 0; } let t = (tm1 + tm2 + 0.5 - 2451545) / 36525, rm_sun = LONGITUDE_SUN (t), //太陽の黄経λsun rm_moon = LONGITUDE_MOON (t), //月の黄経λmoon を計算 delta_rm = rm_moon - rm_sun; //月と太陽の黄経差Δλ (Δλ=λmoon−λsun) if (lc === 1 && delta_rm < 0 ) //ループの1回目(lc=1)で delta_rm < 0.0 の場合には引き込み範囲に補正 delta_rm = NORMALIZATION_ANGLE (delta_rm); else if (0 <= rm_sun && rm_sun <= 20 && 300 <= rm_moon) //春分の近くで朔がある場合(0 ≦λsun≦ 20)で、月の黄経λmoon≧300 の場合には、Δλ= 360.0 − Δλ と計算して補正 delta_rm = 360 - NORMALIZATION_ANGLE (delta_rm); else if (abs (delta_rm) > 40) //Δλの引き込み範囲(±40°)を逸脱した場合には、補正を行う delta_rm = NORMALIZATION_ANGLE (delta_rm); // 時刻引数の補正 (tm -= delta_t) t0 = delta_rm * 29.530589 / 360;//時刻引数の補正値 Δt tm1 -= int (t0); tm2 -= t0 % 1; if (tm2 < 0) { tm2 += 1; tm1 -= 1; } if (30 < lc++)//補正後も振動する場合は、初期値を答えとして返して強制的にループを抜け出して異常終了させる。 return tm + TMZ; //このTMZが増えるのは原本が間違い? } while (abs (t0) > TM_LIMIT); return tm1 + tm2 + TMZ;// 時刻変数の合成(DT ==> JST 変換(補正時刻=0.0sec と仮定して計算)) }, //_________ //角度の正規化(範囲を 0≦θ<360 にする) NORMALIZATION_ANGLE = angle => { return 0 < angle ? angle % 360: 360 + angle % 360; }, //_________ //太陽の黄経 λsun を計算する LONGITUDE_SUN = t => { let th = LONGITUDE_SUN_PARAMETER.map (([a, b, c]) => a * COS (b * t + c)); // 摂動項の計算 return NORMALIZATION_ANGLE (th.reduce (sum, 36000.7695 * t + 280.4659/*比例項*/)); }, //_________ // 月の黄経 λmoon を計算する LONGITUDE_MOON = (t) => { // 摂動項の計算 let th = LONGITUDE_MOON_PARAMETER.map (([a, b, c]) => a * COS (b * t + c)); // 摂動項の計算 return NORMALIZATION_ANGLE (th.reduce (sum, 481267.8809 * t) + 218.3162/*比例項*/); }, //_________ // 年月日、時分秒(世界時)からユリウス日(JD)を計算する // ※ この関数では、グレゴリオ暦法による年月日から求めるものである。 //(ユリウス暦法による年月日から求める場合には使用できない。) YMDT2JD = date => { let [year, month, day, hour = 0, min = 0, sec = 0] = ['getFullYear', 'getMonth', 'getDate' /*, 'getHours', 'getMinutes', 'getSeconds'*/] .map (func => date[func]()); if (++month < 3) { year -= 1; month += 12; } return ( int (365.25 * year) + int (year / 400) - int (year / 100) + int (30.59 * (month - 2)) + 1721088 + day + (sec / 3600 + min / 60 + hour) / 24 ); }, //_________ // ユリウス日(jd)から年月日、時分秒(世界時)を計算する // 戻値 0:年, 1:月, 2:日, 3:時, 4:分, 5:秒 // ※ この関数で求めた年月日は、グレゴリオ暦法によって表されている。 JD2YMDT = jd => { let x0 = int (jd + 68570), x1 = int (x0 / 36524.25), x2 = x0 - int (36524.25 * x1 + .75), x3 = int ((x2 + 1) / 365.2425), x4 = x2 - int (365.25 * x3) + 31, x5 = int (int (x4) / 30.59), x6 = int (int (x5) / 11), y = 100 * (x1 - 49) + x3 + x6, m = x5 - 12 * x6 + 2, d = x4 - int (30.59 * x5), tm = 86400 * (jd % 1); if (m === 2) //# 2月30日の補正 d = (y % (y % 100 ? 400: 4)) ? 29: 28; return [y, m, d, int (tm / 3600), int ((tm % 3600) / 60), tm % 60]; }, //_________ // 太陽の黄経 getSunYellowThrough = tm => { let tm1 = int (tm), // 時刻引数を分解する tm2 = tm - tm1 - TMZ, t = (tm1 + tm2 + 0.5 - 2451545) / 36525; return LONGITUDE_SUN (t); }, //_________ // 今日が24節気かどうか調べる // 戻り値 .... 24節気の名称 check_24sekki = date => { let tm = YMDT2JD (date), rm_sun_today = 15 * int (getSunYellowThrough (tm) / 15), // 今日の太陽の黄経 rm_sun_tommorow = 15 * int (getSunYellowThrough (tm + 1) / 15); // 明日の太陽の黄経 return (rm_sun_today != rm_sun_tommorow) ? SEKKI24_STR_JP[rm_sun_tommorow / 15] : ''; }; //_________ this.get_rokuyou_kanji = get_rokuyou_kanji; this.check_24sekki = check_24sekki; //24節気 this.getKyureki = calc_kyureki; //旧暦を返す } //____________________________________________ const datetimeFunc = ['getFullYear', 'getMonth', 'getDate', 'getDay'], getVal = function (func) { return this[func](); }, Calendar = { current_date: null, init: function () { document.addEventListener ('click', this, false); this.current_date = new Date; this.setCalendarDate (); }, setCalendarDate: function (offset = 0) { let d = this.current_date; d.setMonth (d.getMonth () + offset); this.current_date = d; this.dispCalendar (d); }, dispCalendar: function (date) { let current_date = date, [year, month] = datetimeFunc.map (getVal, current_date), [,,lastDay, lastWeek] = datetimeFunc.map (getVal, new Date (year, month + 1, 0)), indexDate = new Date (year, month, (lastDay % 7) - lastWeek), maxCnt = Math.floor ((lastDay + 6) / 7) * 7, [caption, thead, tbody, tr] = document.querySelectorAll ('caption > label, thead, tbody'); while (tbody.hasChildNodes()) tbody.removeChild (tbody.firstChild); caption.textContent = `${month+1}月 (${year}年)`; for (let i = 0; i < maxCnt; i++) { if (! (i % 7)) tr = tbody.insertRow (); let td = tr.insertCell (), [,m, d] = datetimeFunc.map (getVal, indexDate); td.innerHTML = [ d, get_rokuyou_kanji (indexDate), check_24sekki (indexDate) ].join ('<br>'); td.classList.add ((m === month) ? 'current': m < month ? 'prev': 'next'); indexDate.setDate (indexDate.getDate () + 1); } }, handleEvent: function ({target}) { switch (target.id) { case 'PREV_MONTH' : this.setCalendarDate (-1); break; case 'NEXT_MONTH' : this.setCalendarDate (1); break; } } }; Calendar.init (); </script>