yucatio@システムエンジニア

趣味で作ったものいろいろ

date-fnsを使用してカレンダー作成する(その1: 今月のカレンダーを表示する)

簡単なカレンダーを作成します。date-fnsというJavaScriptの日時ライブラリを使用します。

f:id:yucatio:20191223081351p:plain

デモ : date-fns calendar

要件

  • 今月のカレンダーを表示したい

仕様

  • 今月のカレンダーを表示する
  • 日曜日を行の一番始めにする

今回はこのような表示まで完成させます↓

f:id:yucatio:20191223083802p:plain

事前準備

date-fnsをインストールします。

yarn add date-fns

コード

はじめに、カレンダーに表示するための日付を格納した配列を作成しましょう。 週ごとに日付をまとめます。

import eachDayOfInterval from 'date-fns/eachDayOfInterval'
import endOfWeek from 'date-fns/endOfWeek'
import eachWeekOfInterval from 'date-fns/eachWeekOfInterval'
import startOfMonth from 'date-fns/startOfMonth'
import endOfMonth from 'date-fns/endOfMonth'

const getCalendarArray = date => {
  const sundays = eachWeekOfInterval({
    start: startOfMonth(date),
    end: endOfMonth(date)
  })
  return sundays.map(sunday =>
    eachDayOfInterval({start: sunday, end: endOfWeek(sunday)})
  )
}

const calendar = getCalendarArray(new Date())
console.table(calendar)

実行結果です。ブラウザのコンソールに、カレンダーに表示する日付が表示されました。

f:id:yucatio:20191223083244p:plain

コードの解説

getCalendarArray関数の最初に eachWeekOfInterval を使用しています。

この関数は、startendを渡すと、その期間が含まれる週の始めの日(日曜日)を配列で返します。 例えば、 startに2019年11月1日、endに2019年11月30日を渡すと、

[
  2019年10月27日,
  2019年11月03日,
  2019年11月10日,
  2019年11月17日,
  2019年11月24日
]

を返します(実際にはdate型の配列です)。ちょうど、11月のカレンダーに表示される日曜日と一致しますね。

f:id:yucatio:20191223082532p:plain

この関数に、作成したい月の月初と月末を渡すことで、カレンダーに含まれる日曜日を過不足なく取得することができます。 月初と月末を取得するには、それぞれ startOfMonthendOfMonth を利用します。

日曜日の配列をsundaysに格納します。

ここまでで、以下のプログラムが出来上がりました。

  const sundays = eachWeekOfInterval({
    start: startOfMonth(date),
    end: endOfMonth(date)
  })

次に、日曜日の日付から、その週に含まれる日付を作成します。 週の終わり(土曜日)を endOfWeek で取得し、 eachDayOfInterval で日曜日から土曜日までの日付を取得します。

  sundays.map(sunday =>
    eachDayOfInterval({start: sunday, end: endOfWeek(sunday)})
  )

以上でカレンダー用の日付の2次元配列を作成することができました。

カレンダーを表示する

カレンダーを表示します。 今回はreactを使用します。 create-react-appで新しいプロジェクトを作成しましょう。

npx create-react-app date-fns-calendar
cd date-fns-calendar
yarn add date-fns
yarn start

getCalendarArrayで得られた配列をテーブル形式で表示します。 各週ごとに日付が格納されているので、2重のmapになっています。

日付は getDate で取得します。コンポーネントのキーは曜日にするので、 getDay で取得します。 getDateとgetDayの名前が紛らわしいので気をつけてください。

また、 format を利用してテーブルの上に年月表示します。

src/App.js

import React from 'react'
import format from 'date-fns/format'
import getDate from 'date-fns/getDate'
import getDay from 'date-fns/getDay'
import eachDayOfInterval from 'date-fns/eachDayOfInterval'
import endOfWeek from 'date-fns/endOfWeek'
import eachWeekOfInterval from 'date-fns/eachWeekOfInterval'
import startOfMonth from 'date-fns/startOfMonth'
import endOfMonth from 'date-fns/endOfMonth'

const getCalendarArray = date => {
  const sundays = eachWeekOfInterval({
    start: startOfMonth(date),
    end: endOfMonth(date)
  })
  return sundays.map(sunday =>
    eachDayOfInterval({start: sunday, end: endOfWeek(sunday)})
  )
}

function App() {
  const targetDate = new Date()
  const calendar = getCalendarArray(targetDate)

  return (
    <div>
      {format(targetDate, 'y年M月')}
      <table>
        <thead>
          <tr>
            <th>日</th><th>月</th><th>火</th><th>水</th><th>木</th><th>金</th><th>土</th>
          </tr>
        </thead>
        <tbody>
          {calendar.map((weekRow, rowNum) => (
            <tr key={rowNum}>
              {weekRow.map(date => (
                <td key={getDay(date)}>{getDate(date)}</td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  )
}

export default App

実行結果です。今月のカレンダーが表示されました。

f:id:yucatio:20191223083802p:plain

前の月と次の月の日付も表示されていますが、今月以外の日付の見た目を変更するのは3つ先の記事で行いますので、今回はこのままです。

ソースコード

ここまでのソースコードです。

GitHub - yucatio/date-fns-calendar at 00c2d86e0307650be1a9ded334797db2ec7b8eb6

環境

  • npm: 6.12.1
  • react: 16.12.0
  • date-fns: 2.8.1

あとがき

以前にrubyで同様のプログラムを作成しました。

yucatio.hatenablog.com

同様のアルゴリズムを使用しようと思いましたが、 eachWeekOfInterval という関数を見つけたことで大幅にコード量を減らせました。

次回の記事

次回は次の月と前の月に移動できるようにします。

yucatio.hatenablog.com