用 dayjs 得到一个日历的数据

需求

为了写一个自定义的日历组件,所以需要生成一个日历数据

思路

dayjs 是一个便利的日期库,和 moment 的 API 一样,首先可以用它来获得一些基础的日历信息,那些我并不想自己来计算。

为了生成日历数据,要先得到一个日期 date,然后为了方便进行日期计算,所以将 date 先转换成 dayjs 的对象,这里传入的 date 已经是 dayjs 的对象

获得基础信息

javascript 复制代码
function generateCalendar(date, options = {
  weekStartsOnMonday: true,
}) {
  // 最终所需的data
  const calendar = [];
  // 今天的日期
  const today = dayjs();
  // 传入日期的年份
  const year = date.year();
  // 传入日期的月份(0-11,所以要+1)
  const month = date.month() + 1;
  // 传入日期的那个月有多少天
  const daysInMonth = date.daysInMonth();
  // 传入日期的那个月的第一天
  const monthFirstDay = date.startOf("month");
  // 传入日期的那个月的最后一天
  const monthEndDay = date.endOf("month");
  // 周起始日是周一还是周日
  const weekStartDay = options.weekStartsOnMonday ? 1 : 0;
}

星期的起始

众所周知,日历可以配置星期起始于周一或者周日,所以在生成时要考虑到。dayjs 的.day()方法可以拿到该日期是星期几,值为0-6,0代表星期日,所以为了方便我自己理解,写一个获得.day()对应的周索引的函数,1是一周的第一天,7是一周的最后一天

javascript 复制代码
function getWeekIndex(day, startDay) {
  return (day - startDay + 7) % 7 + 1
}

例如获得到了一个日期是星期三,周起始日是周日,那么 weekIndex 就是 4,代表在这一周星期三位于第四个位置

javascript 复制代码
// 当前月份第一天在周中是第几天
const monthFirstDayWeekIndex = getWeekIndex(monthFirstDay.day(), weekStartDay);
// 当前月份最后一天在周中是第几天
const monthEndDayWeekIndex = getWeekIndex(monthEndDay.day(), weekStartDay);

生成前一个月的数据

有了上面的基本信息,就可以开始生成日历数据了

首先是生成前一个月的数据,当月份第一天并非一周的开始日时,就会有上个月的日期数据

javascript 复制代码
// 上个月的数据
const prevMonthDays = [];
if (monthFirstDayWeekIndex !== 1) {
  // 将这个月第一天减去1天,就拿到了上个月最后一天
  let prevMonthLastDate = monthFirstDay.subtract(1, 'day').date();
  const prevMonthYear = date.subtract(1, 'month').year();
  const prevMonthMonth = date.subtract(1, 'month').month() + 1;
  // 根据当前月份第一天在第几位,计算上个月份的天数
  for (let prevMonthWeekIndex = monthFirstDayWeekIndex - 1; prevMonthWeekIndex > 0; prevMonthWeekIndex--) {
    prevMonthDays.push({
      year: prevMonthYear,
      month: prevMonthMonth,
      date: prevMonthLastDate,
      day: getWeekDay(prevMonthWeekIndex, weekStartDay),
      isCurrentMonth: false,
      isPrevMonth: true,
      fullDate: `${prevMonthYear}-${padDate(prevMonthMonth)}-${padDate(prevMonthLastDate)}`
    });
    prevMonthLastDate--;
  }
  prevMonthDays.reverse();
}

假设这个月第一天是第一周的第三天,代表有两天的上个月的数据,所以在计算上个月数据时,是倒着添加进数组中的,所以在全部添加完毕后,需要将数组倒转

此处有两个函数,一个是getWeekDay,作用是根据周索引和周起始日来获取这一天是周几

javascript 复制代码
const daysOfWeek = [7, 1, 2, 3, 4, 5, 6];
function getWeekDay(index, startDay) {
  return daysOfWeek[startDay + index - 1];
}

第二个是padDate,很简单就是为不足10的日期添加一个0

javascript 复制代码
function padDate(date) {
  return String(date).padStart(2, '0');
}

生成下一个月的数据

因为日历数据中每周都有7天,所以会有当本月结束后,下个月的数据填补的情况

javascript 复制代码
// 当前日历的周数
const calendarLength = Math.ceil((monthFirstDayWeekIndex - 1 + daysInMonth) / 7);
// 下个月的数据
const nextMonthDays = [];
// 当前日历去掉上月数据和当月数据的剩余天数
const nextMonthDaysLength = calendarLength * 7 - daysInMonth - prevMonthDays.length;
if (nextMonthDaysLength > 0) {
  const nextMonthYear = date.add(1, 'month').year();
  const nextMonthMonth = date.add(1, 'month').month() + 1;
  // 根据当前月份还剩下的天数,计算下个月份的天数
  for (let nextMonthDate = 1; nextMonthDate <= nextMonthDaysLength; nextMonthDate++) {
    nextMonthDays.push({
      year: nextMonthYear,
      month: nextMonthMonth,
      date: nextMonthDate,
      day: getWeekDay(monthEndDayWeekIndex + nextMonthDate, weekStartDay),
      isCurrentMonth: false,
      isNextMonth: true,
      fullDate: `${nextMonthYear}-${padDate(nextMonthMonth)}-${padDate(nextMonthDate)}`
    });
  }
}

生成日历数据

javascript 复制代码
let currentMonthDate = 1;
for (let w = 1; w <= calendarLength; w++) {
  const week = [];
  // 在第一周时将上个月的数据添加进来
  if (w === 1) {
    week.push(...prevMonthDays);
  }
  for (let wi = week.length + 1; wi <= 7; wi++) {
    // 本月数据全部生成后就可以停止了
    if (currentMonthDate > daysInMonth) {
      break;
    }
    const day = {
      year,
      month,
      date: currentMonthDate,
      day: getWeekDay(wi, weekStartDay),
      isCurrentMonth: true,
      fullDate: `${year}-${padDate(month)}-${padDate(currentMonthDate)}`,
    }
    day.isToday = today.isSame(day.fullDate, 'day')
    week.push(day);
    currentMonthDate++;
  }
  calendar.push(week);
}
// 将下个月的数据添加进最后一周
calendar[calendar.length - 1].push(...nextMonthDays);

这样,日历的数据就全部得到了

相关推荐
dlnu20152506222 小时前
ssr实现方案
前端·javascript·ssr
轻口味3 小时前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王4 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发4 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
真滴book理喻7 小时前
Vue(四)
前端·javascript·vue.js
程序员_三木8 小时前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
开心工作室_kaic9 小时前
springboot476基于vue篮球联盟管理系统(论文+源码)_kaic
前端·javascript·vue.js
川石教育9 小时前
Vue前端开发-缓存优化
前端·javascript·vue.js·缓存·前端框架·vue·数据缓存
搏博9 小时前
使用Vue创建前后端分离项目的过程(前端部分)
前端·javascript·vue.js
温轻舟9 小时前
前端开发 之 12个鼠标交互特效上【附完整源码】
开发语言·前端·javascript·css·html·交互·温轻舟