JavaScript
date
TypeScript
フロントエンド
React
0
どのような問題がありますか?

投稿日

Dateオブジェクトだけでカレンダーを作る

概要

既存のカレンダーライブラリはたくさんありますが、少々柔軟性に欠けるのでイチから自分で作ろうと考えました。今回はJavaScriptの標準組み込みオブジェクトである「Dateオブジェクト」のみを使ってカレンダー生成アルゴリズムを構築していきます。(マイブームのReactで開発を進めます。)

完成したサンプルサイトはこちら↓↓

アルゴリズム

年,月を引数にとり、その月の日にちをカレンダーに見立てた二次元配列で返す関数を作ります。
以下は2022年10月の配列です。

calendar:number[][]
[
    [25, 26, 27, 28, 29, 30, 1],
    [2,  3,  4,  5,  6,  7,  8],
    [9, 10, 11, 12, 13, 14, 15],
    [16, 17, 18, 19, 20, 21, 22],
    [23, 24, 25, 26, 27, 28, 29],
    [30, 31,  1,  2,  3,  4,  5]
]

この配列をmapでループ処理にかけてカレンダーを表示します。

Dateオブジェクトの使い方

JavaScript の Date オブジェクトは、単一の瞬間の時刻をプラットフォームに依存しない形式で表します。 Date オブジェクトは協定世界時 (UTC) の 1970 年 1 月 1 日からの経過ミリ秒数を表す Number の値を含んでいます。

Date - JavaScript | MDN

任意の時刻の取得

2022年10月1日の情報を取得します。

const day = new Date(2022, 10, 1);

引数を渡さなければ現在時刻を取得できます。

const day = new Date();

使用するメソッド

const today = new Date();

const year  = today.getFullYear()   //年を取得
const month = today.getMonth()      //月を取得
const date  = today.getDate()       //日を取得
const day   = today.getDay()        //曜日を取得

getDay()メソッドは日曜〜土曜に対応して0~6を返します。

手順

Date オブジェクトを駆使してカレンダーの配列を生成します。

各種宣言

Make_calendar.ts
let calendar:number[][] = []                //カレンダー
const first_day = new Date(year, month, 1); //任意の月の1日を取得
let week:number[] = []                      //週生成用

第一週を生成

任意の月の第一週を生成してカレンダーにpushします。
Date関数の第3引数に0を渡すと前の月の最終日を取得できます。

Make_calendar.ts
for(let i = first_day.getDay(); i < 7; i++){
    week.push(i - first_day.getDay() + 1)
}

const end_last_month = new Date(first_day.getFullYear(), first_day.getMonth(), 0)
for(let i = 0; i < first_day.getDay(); i++){
    week.unshift( end_last_month.getDate() - i)
}

calendar.push(week)

最終週を生成

Make_calendar.ts
const last_day = new Date(first_day.getFullYear(), first_day.getMonth() + 1, 0)
let last_week:number[] = []

for(let i = 0; i <= last_day.getDay(); i++){
    last_week.unshift(last_day.getDate() - i)
}

if(last_day.getDay() !== 6){
    const begin_next_month = new Date(first_day.getFullYear(), first_day.getMonth() + 1, 1)
    for(let i = 0; i < 7 - begin_next_month.getDay(); i++){
        last_week.push( begin_next_month.getDate() + i)
    }
}

中間週を生成

中間週を生成してカレンダーにpushしていきます。最終週に到達したら先ほど生成したlast_week[]をpushしましょう。

Make_calendar.ts
for(let i = 0; i < 5; i++){
    week = []
    for(let j = 0; j < 7 ; j++){
        week.push((8 - first_day.getDay() + j) + 7 * i)
    }
    if(week[0] !== last_week[0]){
        calendar.push(week)
    }else{
        calendar.push(last_week)
        break
    }
}

関数全体

Make_calendar.ts
const Make_calendar = (year:number, month:number):number[][] => {
    let calendar:number[][] = []                //カレンダー
    const first_day = new Date(year, month, 1); //任意の月の1日を取得
    let week:number[] = []                      //週生成用

    // first week========
    for(let i = first_day.getDay(); i < 7; i++){
        week.push(i - first_day.getDay() + 1)
    }

    const end_last_month = new Date(first_day.getFullYear(), first_day.getMonth(), 0)
    for(let i = 0; i < first_day.getDay(); i++){
        week.unshift( end_last_month.getDate() - i)
    }
    calendar.push(week)

    // last week============
    const last_day = new Date(first_day.getFullYear(), first_day.getMonth() + 1, 0)
    let last_week:number[] = []

    for(let i = 0; i <= last_day.getDay(); i++){
        last_week.unshift(last_day.getDate() - i)
    }

    if(last_day.getDay() !== 6){
        const begin_next_month = new Date(first_day.getFullYear(), first_day.getMonth() + 1, 1)
        for(let i = 0; i < 7 - begin_next_month.getDay(); i++){
            last_week.push( begin_next_month.getDate() + i)
        }
    }

    // middle week===========
    for(let i = 0; i < 5; i++){
        week = []
        for(let j = 0; j < 7 ; j++){
            week.push((8 - first_day.getDay() + j) + 7 * i)
        }
        if(week[0] !== last_week[0]){
            calendar.push(week)
        }else{
            calendar.push(last_week)
            break
        }
    }
    // ======================

    return calendar
}

コンポーネント設計

React × Typescript で失礼します。

const Square = (props):JSX.Element => {
    return(
        <div>
            {props.date}
        </div>
    )
}

const Column = (props):JSX.Element => {
    return(
        <div>
            {props.week.map((date) => <Square date = {date} />)}
        </div>
    )
}

const Calendar = (props):JSX.Element => {

    let calendar:number[][] = Make_calendar(props.year, props.month)

    return(
        <div>
            {calendar.map((week) => <Column week = {week} />)}
        </div> 
    )
}

const Home = ():JSX.Element => {
    const today = new Date();
    const [year, setYear] = useState<number>(Number(today.getFullYear()))
    const [month, setMonth] = useState<number>(Number(today.getMonth()))
    
    return(
        <div>
            <Calendar 
                year = {year}
                month = {month}
            />
        </div>
    )
}

完成

完成したサンプルサイトはこちら↓↓

まとめ

Dateオブジェクトだけでカレンダーを作ってみました。カレンダー配列生成マシンはそこそこ便利なのでぜひ参考にしてください。

参考

新規登録して、もっと便利にQiitaを使ってみよう

  1. あなたにマッチした記事をお届けします
  2. 便利な情報をあとで効率的に読み返せます
ログインすると使える機能について
hirokiwa

コメント

この記事にコメントはありません。
あなたもコメントしてみませんか :)
新規登録
すでにアカウントを持っている方はログイン
記事投稿イベント開催中
PHP強化月間~開発する上で知っておくべき知見を共有しよう~
~
フロントエンドの開発効率を向上するヒントを教え合おう!
~
0
どのような問題がありますか?
新規登録して、Qiitaをもっと便利に使ってみませんか

この機能を利用するにはログインする必要があります。ログインするとさらに下記の機能が使えます。

  1. ユーザーやタグのフォロー機能であなたにマッチした記事をお届け
  2. ストック機能で便利な情報を後から効率的に読み返せる
新規登録ログイン
ストックするカテゴリー