カレンダー生成

生成の流れ

  1. 対象月の日数を調べる
  2. カレンダー上で、1日の前に何日(何マス)あるか
  3. カレンダー上で、最終日の後ろに何日(何マス)あるか
  4. カレンダーに表示される日数を調べる
    • 前月、翌月も含んだ数
  5. 日数分の配列を生成
  6. chunkする
    • 最終的に[Array(7), Array(7), Array(7), Array(7), Array(7)]のような配列になる

サンプル

週の開始を変更したい場合

import chunk from 'lodash/fp/chunk';
import compose from 'lodash/fp/compose';
import map from 'lodash/fp/map';
import range from 'lodash/fp/range';
import * as moment from 'moment';

const DAYS_PER_WEEK = 7;

export function createCalendar(
  m = moment(),

  // 週の最初の曜日
  // default(0)は日曜
  startDayOfWeek = 0) {

  // 指定した月の1日
  const startOfMonth: moment.Moment = m.clone().startOf('month');

  // 月の日数
  const daysInMonth: number = m.clone().endOf('month').date();

  // カレンダー上で、1日の前に何日(何マス)あるか
  const daysBefore: number = getDaysBefore(startOfMonth, startDayOfWeek);

  // カレンダー上で、最終日の後ろに何日(何マス)あるか
  const daysAfter: number = getDaysAfter(daysBefore, daysInMonth);

  // カレンダーに表示する日数分の配列を生成
  const diffs: number[] = getDiffs(daysBefore, daysInMonth, daysAfter);

  return compose(
    chunk(DAYS_PER_WEEK),
    map((diff) => startOfMonth.clone().add(diff, 'd'))
  )(diffs);
}

function getDaysBefore(startOfMonth: moment.Moment, startDayOfWeek: number): number {
  const dayOfWeek = startOfMonth.day();
  return (dayOfWeek + DAYS_PER_WEEK - startDayOfWeek) % DAYS_PER_WEEK;
  // if (dayOfWeek > startDayOfWeek) {
  //   return dayOfWeek - startDayOfWeek;
  // } else {
  //   return dayOfWeek + DAYS_PER_WEEK - startDayOfWeek;
  // }
}

function getDaysAfter(daysBefore: number, daysInMonth: number): number {
  return DAYS_PER_WEEK - (daysBefore + daysInMonth) % DAYS_PER_WEEK;
}

function getDiffs(daysBefore, daysInMonth, daysAfter): number[] {
  return range((daysBefore * -1), daysInMonth + daysAfter)
}