date-fnsを使用してカレンダー作成する(その1: 今月のカレンダーを表示する)
簡単なカレンダーを作成します。date-fnsというJavaScriptの日時ライブラリを使用します。
デモ : date-fns calendar
要件
- 今月のカレンダーを表示したい
仕様
- 今月のカレンダーを表示する
- 日曜日を行の一番始めにする
今回はこのような表示まで完成させます↓
事前準備
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)
実行結果です。ブラウザのコンソールに、カレンダーに表示する日付が表示されました。
コードの解説
getCalendarArray
関数の最初に
eachWeekOfInterval
を使用しています。
この関数は、start
とend
を渡すと、その期間が含まれる週の始めの日(日曜日)を配列で返します。
例えば、
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月のカレンダーに表示される日曜日と一致しますね。
この関数に、作成したい月の月初と月末を渡すことで、カレンダーに含まれる日曜日を過不足なく取得することができます。 月初と月末を取得するには、それぞれ startOfMonth と endOfMonth を利用します。
日曜日の配列を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
実行結果です。今月のカレンダーが表示されました。
前の月と次の月の日付も表示されていますが、今月以外の日付の見た目を変更するのは3つ先の記事で行いますので、今回はこのままです。
ソースコード
ここまでのソースコードです。
GitHub - yucatio/date-fns-calendar at 00c2d86e0307650be1a9ded334797db2ec7b8eb6
環境
- npm: 6.12.1
- react: 16.12.0
- date-fns: 2.8.1
あとがき
以前にrubyで同様のプログラムを作成しました。
同様のアルゴリズムを使用しようと思いましたが、 eachWeekOfInterval という関数を見つけたことで大幅にコード量を減らせました。
次回の記事
次回は次の月と前の月に移動できるようにします。