カレンダーは随分前に作ったことがありますが、現在の情報を追加して作り直してみました。
See the Pen カレンダー(月/15週/年) by Ikiuo (@ikiuo) on CodePen.
Google Chrome 拡張機能
CodePen のものを少し弄って Google Chrome 拡張機能にしてみました。
Date の派生クラス
意外と面倒臭い休日処理
- 春分と秋分の日
- 大雑把な方法で計算しています
- ハッピーマンデー
- 休日と休日の間が休日になる
- 休日が日曜のときの代替
取得したい日とその前後くらいでは決定できないので、週初めから翌週の日曜までを使って決定しています。
/*
* 休日データ
*/
class JHoliday {
static #startDate = (new Date(1970, 1-1, 1)).getTime() * 10;
static #offsetVE = JHoliday.#startDate + 68619916800;
static #offsetAE = JHoliday.#startDate + 229674407040;
static #yEquinox = 315569255616;
static #monthTable = [...Array(12)].map(() => ({}));
static #createParameter(name, month, day, start, end) {
const S = JHoliday;
month -= 1;
const holidy = {
name: name,
month: month,
day: day,
start: Math.max(1948, (start ?? 0)),
end: end ?? 10000,
}
const table = S.#monthTable;
const mtab = table[month];
if (!mtab[day])
mtab[day] = [];
mtab[day].push(holidy);
return holidy;
}
static #mainTable = [
['元日', 1, 1],
['成人の日', 1, 15, 0, 1999],
['成人の日', 1, 'M2', 2000],
['建国記念の日', 2, 11],
['春分の日', 3, 'VE'],
['憲法記念日', 5, 3],
['みどりの日', 5, 4, 2007],
['こどもの日', 5, 5],
['海の日', 7, 20, 1996, 2002],
['海の日', 7, 'M3', 2003],
['山の日', 8, 11, 2016],
['敬老の日', 9, 15, 1966, 2002],
['敬老の日', 9, 'M3', 2003],
['秋分の日', 9, 'AE'],
['体育の日', 10, 10, 0, 1999],
['体育の日', 10, 'M2', 2000, 2019],
['スポーツの日', 10, 'M2', 2020],
['文化の日', 11, 3],
['勤労感謝の日', 11, 23],
/* 昭和 */
['天皇誕生日', 4, 29, 0, 1988],
['みどりの日', 4, 29, 1989, 2006],
['昭和の日', 4, 29, 2007],
/* 平成 */
['天皇誕生日', 12, 23, 1989, 2018],
/* 令和 */
['天皇の即位の日', 5, 1, 2019, 2019],
['即位礼正殿の儀が行われる日', 10, 22, 2019, 2019],
['天皇誕生日', 2, 23, 2020],
].map((v) => JHoliday.#createParameter(...v));
static #altname = '休日';
static #getBasic(date) {
const S = JHoliday;
const year = date.getFullYear();
const month = date.getMonth();
const mday = date.getDate();
const wday = date.getDay();
const mtab = S.#monthTable[month]
const dtab = mtab[mday];
if (dtab) {
const t = dtab.filter((h) =>
(h.start <= year && year <= h.end));
if (t.length)
return t[0].name;
}
if (wday == 1) {
const mweek = Math.trunc((mday - 1) / 7);
const monday = mtab[`M${mweek+1}`];
if (monday) {
const t = monday.filter((h) =>
(h.start <= year && year <= h.end));
if (t.length)
return t[0].name;
}
}
const ve = mtab['VE'];
if (ve) {
const equniox = S.getVernalEquinox(year);
if (mday == equniox.getDate())
return ve[0].name;
}
const ae = mtab['AE'];
if (ae) {
const equniox = S.getAutumnEquinox(year);
if (mday == equniox.getDate())
return ae[0].name;
}
return undefined;
}
static #weekCache = {}
static #getWeek(date) {
const S = JHoliday;
const year = date.getFullYear();
const month = date.getMonth();
const mday = date.getDate();
const wday = date.getDay();
const wtop = new Date(year, month, 1).getDay();
const mweek = Math.trunc((wtop + mday - 1) / 7);
const index = (year * 12 + month) * 6 + mweek;
const cache = S.#weekCache[index];
if (cache)
return cache;
const line = [...Array(8)].map((_, i) =>
S.#getBasic(new Date(year, month, mday - wday + i)));
if (year >= 1973 && line[0]) {
for (let i = 1; i < 7; i++) {
if (!line[i]) {
line[i] = S.#altname;
break;
}
}
}
if (year >= 1986)
for (let i = 1; i < 7; i++)
if (!line[i] && line[i - 1] && line[i + 1])
line[i] = S.#altname;
S.#weekCache[index] = line;
return line;
}
// 年から春分の日(Date型)を取得.
static getVernalEquinox(year) {
const S = JHoliday;
return new Date(((year - 1970) * S.#yEquinox + S.#offsetVE) / 10);
}
// 年から秋分の日(Date型)を取得.
static getAutumnEquinox(year) {
const S = JHoliday;
return new Date(((year - 1970) * S.#yEquinox + S.#offsetAE) / 10);
}
// Date 型から休日を取得.
static getHoliday(date) {
const S = JHoliday;
return S.#getWeek(date)[date.getDay()];
}
}
/*
* 休日ありの Date 派生クラス
*/
class JHDate extends Date {
#holiday;
constructor() {
super(...arguments);
this.#holiday = JHoliday.getHoliday(this);
}
// 休日を取得.
getHoliday() {
return this.#holiday;
}
}