LoginSignup
0
0

Date に休日情報を追加した派生クラスを作ってカレンダーを表示する

Last updated at Posted at 2024-01-27

カレンダーは随分前に作ったことがありますが、現在の情報を追加して作り直してみました。

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;
    }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0