LoginSignup
2
1

More than 3 years have passed since last update.

【JavaScript】祝日取得クラス

Last updated at Posted at 2020-07-13

先日作成したPHP版祝日取得クラス のJavaScript版です。
PHP版をJavaScriptに置き換えただけですので、使い方もほぼ同じです。
メソッドやプロパティについてはPHP版のほうを参照してください。

holiday_class.js
class Holiday {

    constructor(year = 0) {
        // 祝日定義
        // ('国民の祝日に関する法律'が公布・施行された1948年7月20日以降のもののみ)
        this.holidayDefinitions = {
            1 : {
                '1949:1'    : '元日',
                '1949:15, 2000:2_1'
                            : '成人の日',
            },
            2 : {
                '1967:11'   : '建国記念の日',
                '2020:23'   : '天皇誕生日',
                '1989:24, 1990:0'
                            : '大喪の礼',
            },
            3 : {
                '1949:s'    : '春分の日',
            },
            4 : {
                '1959:10, 1960:0'
                            : '結婚の儀',
                '1949:29'   : '天皇誕生日, 1989:みどりの日, 2007:昭和の日',
            },
            5 : {
                '2019:1, 2020:0'
                            : '皇太子殿下即位・改元',
                '1949:3'    : '憲法記念日',
                '2007:4'    : 'みどりの日',
                '1949:5'    : 'こどもの日',
            },
            6 : {
                '1993:9, 1994:0'
                            : '結婚の儀',
            },
            7 : {
                '1996:20, 2003:3_1, 2020:23, 2021:22, 2022:3_1'
                            : '海の日',
                '2020:24, 2021:23, 2022:0'
                            : 'スポーツの日',
            },
            8 : {
                '2016:11, 2020:10, 2021:8, 2022:11'
                            : '山の日',
            },
            9 : {
                '1966:15, 2003:3_1'
                            : '敬老の日',
                '1948:a'    : '秋分の日',
            },
            10 : {
                '1966:10, 2000:2_1, 2020:0, 2022:2_1'
                            : '体育の日, 2020:スポーツの日',
                '2019:22, 2020:0'
                            : '即位礼正殿の儀',
            },
            11 : {
                '1948:3'    : '文化の日',
                '1990:12, 1991:0'
                            : '即位礼正殿の儀',
                '1948:23'   : '勤労感謝の日',
            },
            12 : {
                '1989:23, 2019:0'
                            : '天皇誕生日',
            },
        };

        this.dateTime = new Date();
        this.dateTime.setHours(0, 0, 0);

        if(year < 1) year = this.dateTime.getFullYear();
        this.year = year;
        this.month = this.dateTime.getMonth() + 1;

        this.result = {};
        this.useIndefiniteHoliday = 1;
        this.resultType = 0;
    }

    /**
     *  1年分のリストを返す
     */
    getHolidayOfYear(year = 0) {
        if(year < 1) year = this.year;

        if(this.result[year] === undefined) {
            // 該当年の祝日オブジェクトに変換
            let holiday = {};
            const equinox = .242194 * (year - 1980) - Math.floor((year - 1980) / 4);
            for(const month in this.holidayDefinitions) {
                holiday[month] = {};
                const currentMonthData = this.holidayDefinitions[month];
                for(const days in currentMonthData) {
                    const names = currentMonthData[days];
                    // 対象年・日取得
                    const _days = days.split(',');
                    const arrTmp = {};

                    for(const i in _days) {
                        const tmp = _days[i].split(':');
                        arrTmp[Number(tmp[0])] = tmp[1].trim();
                    }
                    let yearTmp = 0;
                    for(const tmp in arrTmp)
                        if(year >= tmp && tmp >= yearTmp) yearTmp = tmp;

                    if(yearTmp === 0) continue;

                    // 日を記述形式ごとに取得
                    let day;
                    if(arrTmp[yearTmp] === 's')
                        day = Math.floor(20.8431 + equinox);
                    else if(arrTmp[yearTmp] === 'a')
                        day = Math.floor(23.2488 + equinox);
                    else if(arrTmp[yearTmp].indexOf('_') >= 0) {
                        const [num, w] = arrTmp[yearTmp].split('_');
                        day = this.getDayOfNumWeek(year, month, num, w);
                    }
                    else
                        day = Number(arrTmp[yearTmp]);

                    if(day < 1) continue;

                    // 名称取得
                    const _names = names.split(',');
                    const arrTmpName = {};
                    for(const i in _names) {
                        const tmp = _names[i].split(':');
                        if(tmp.length === 1)
                            arrTmpName[0] = tmp[0].trim();
                        else
                            arrTmpName[Number(tmp[0])] = tmp[1].trim();
                    }

                    yearTmp = 0;
                    for(const tmp in arrTmpName)
                        if(year >= tmp && tmp >= yearTmp) yearTmp = tmp;

                    holiday[month][day] = (holiday[month][day] === undefined) ?
                        arrTmpName[yearTmp] : holiday[month][day] + ', ' + arrTmpName[yearTmp];
                }
            }

            // 国民の休日・振替休日
            if(this.useIndefiniteHoliday)
                holiday = this.indefiniteHoliday(holiday, year);

            this.result[year] = holiday;
        }

        return this.resultType ?
            this.convertLinear(year, this.result[year]):
            this.result[year];
    }

    /**
     *  国民の休日・振替休日
     */
    indefiniteHoliday(holiday, year) {
        for(let month = 1; month <= 12; month++) {
            // 月末日
            this.dateTime.setFullYear(year, month, 0);
            const lastDay = this.dateTime.getDate();

            for(let day = 1; day <= lastDay; day++) {
                // 前日の月日
                this.dateTime.setFullYear(year, month - 1, day - 1);
                const prevMonth = this.dateTime.getMonth() + 1;
                const prevDay = this.dateTime.getDate();

                // 翌日の月日
                this.dateTime.setFullYear(year, month - 1, day + 1);
                const nextMonth = this.dateTime.getMonth() + 1;
                const nextDay = this.dateTime.getDate();

                // 祝日に挟まれた平日を国民の休日に変更(1986年以降)
                this.dateTime.setFullYear(year, month - 1, day);
                if( year >= 1986 && // 1986年以降
                    holiday[prevMonth][prevDay] !== undefined && // 前日が祝日
                    holiday[nextMonth][nextDay] !== undefined && // 翌日が祝日
                    holiday[month][day] === undefined && // 当日が祝日ではない
                    this.dateTime.getDay() !== 0 // 当日が日曜ではない
                ) {
                    holiday[month][day] = '国民の休日';
                }

                // 振替休日(1973年4月以降)
                if( (year > 1973 || (year == 1973 && month >= 4)) && // 1973年4月以降
                    holiday[month][day] !== undefined && // 祝日
                    this.dateTime.getDay() === 0 // 日曜
                ) {
                    // その日以降の直近の平日を振替休日に
                    for(let i = 1; i < 7; i++) {
                        this.dateTime.setFullYear(year, month - 1, day + i);
                        const m = this.dateTime.getMonth() + 1;
                        const d = this.dateTime.getDate();
                        if(holiday[m][d] === undefined) {
                            holiday[m][d] = '振替休日';
                            break;
                        }
                    }
                }
            }
        }
        return holiday;
    }

    /**
     *  year年 month月 第num w曜日に該当する日を返す
     */
    getDayOfNumWeek(year, month, num, w) {
        this.dateTime.setFullYear(year, month - 1, 1);
        const firstDayWeek = this.dateTime.getDay();
        return 1 + (num - 1) * 7 + (7 + w - firstDayWeek) % 7;
    }

    /**
     *  YYYY-MM-DDをキーとしたオブジェクトに変換
     */
    convertLinear(year, array) {
        const arrTmp = {};
        for(const month in array) {
            for(const day in array[month]) {
                const names = array[month][day];
                arrTmp[
                    String(year).padStart(4, '0') + '-' +
                    String(month).padStart(2, '0') + '-' +
                    String(day).padStart(2, '0')
                ] = names;
            }
        }
        return arrTmp;
    }

    /**
     *  1か月分のリストを返す
     */
    getHolidayOfMonth(month = 0) {
        if(month < 1 || month > 12) month = this.month;

        const year = this.year;

        // 該当年の結果が未取得であれば取得
        if(this.result[year] === undefined)
            this.getHolidayOfYear();

        if(this.resultType) {
            const arrTmp = {};
            for(const day in this.result[year][month]) {
                arrTmp[
                    String(year).padStart(4, '0') + '-' +
                    String(month).padStart(2, '0') + '-' +
                    String(day).padStart(2, '0')
                ] = this.result[year][month][day];
            }
            return arrTmp;
        } else {
            return this.result[year][month];
        }
    }

    /**
     *  国民の休日・振替休日 使用フラグ変更
     */
    setUseIndefiniteHoliday(flg = 1) {
        this.useIndefiniteHoliday = flg == 1 ? 1 : 0;

        // 取得済みの結果をリセット
        this.result = {};
    }

    /**
     *  戻り値形式変更
     */
    setResultType(flg = 0) {
        this.resultType = flg == 0 ? 0 : 1;
    }

    /**
     *  年変更
     */
    setYear(year = 0) {
        if(year < 1) year = new Date().getFullYear();
        this.year = year;
    }
}

使用例

const holiday = new Holiday;
// 年も同時に指定
//const holiday = new Holiday(2019);

// 国民の休日及び振替休日の使用指定(0:非使用 1:使用(デフォルト))
holiday.setUseIndefiniteHoliday(1);

// 戻り値形式変更メソッド
// 0: 月、日をキーとしたオブジェクト(デフォルト)
// 1: YYYY-MM-DDをキーとしたオブジェクト
holiday.setResultType(0);

// 1年分のリスト取得
// 引数は年、引数省略時は年プロパティの値を参照します
console.log(holiday.getHolidayOfYear());

// 1か月分のリスト取得
// 引数は月、引数省略時は実行時の月で取得します
// 年は年プロパティの値を参照します
console.log(holiday.getHolidayOfMonth());

// 戻り値をYYYY-MM-DD形式に変更
holiday.setResultType(1);

// 年プロパティ変更(引数省略時は現在の年で設定)
holiday.setYear(2019);

console.log(holiday.getHolidayOfYear());
console.log(holiday.getHolidayOfMonth(5));

結果

result.jpg

年、月をキーにして取得したものは、こんな使い方をする場合に楽かと思います。

const year  = 2020;
const month = 7;
const day   = 24;
const holidayName = new Holiday(year).getHolidayOfMonth(month)[day];
console.log(
    `${year}${month}${day}日は` +
    (holidayName !== undefined ? `${holidayName}です` : '祝日ではありません')
);

結果

2020年7月24日はスポーツの日です
2
1
1

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
2
1