概要
既存のカレンダーライブラリはたくさんありますが、少々柔軟性に欠けるのでイチから自分で作ろうと考えました。今回はJavaScriptの標準組み込みオブジェクトである「Dateオブジェクト」のみを使ってカレンダー生成アルゴリズムを構築していきます。(マイブームのReactで開発を進めます。)
完成したサンプルサイトはこちら↓↓
アルゴリズム
年,月を引数にとり、その月の日にちをカレンダーに見立てた二次元配列で返す関数を作ります。
以下は2022年10月の配列です。
[
[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 の値を含んでいます。
任意の時刻の取得
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 オブジェクトを駆使してカレンダーの配列を生成します。
各種宣言
let calendar:number[][] = [] //カレンダー
const first_day = new Date(year, month, 1); //任意の月の1日を取得
let week:number[] = [] //週生成用
第一週を生成
任意の月の第一週を生成してカレンダーにpushします。
Date関数の第3引数に0を渡すと前の月の最終日を取得できます。
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)
最終週を生成
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しましょう。
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
}
}
関数全体
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オブジェクトだけでカレンダーを作ってみました。カレンダー配列生成マシンはそこそこ便利なのでぜひ参考にしてください。
参考