用 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);

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

相关推荐
pe7er38 分钟前
使用 Vue 官方脚手架创建项目时遇到 Node 18 报错问题的排查与解决
前端·javascript·vue.js
pe7er1 小时前
使用 types / typings 实现全局 TypeScript 类型定义,无需 import/export
前端·javascript·vue.js
islandzzzz1 小时前
(第二篇)HMTL+CSS+JS-新手小白循序渐进案例入门
前端·javascript·css·html
喝拿铁写前端1 小时前
前端实战优化:在中后台系统中用语义化映射替代 if-else,告别魔法数字的心智负担
前端·javascript·架构
超人不会飛2 小时前
就着HTTP聊聊SSE的前世今生
前端·javascript·http
蓝胖子的多啦A梦2 小时前
Vue+element 日期时间组件选择器精确到分钟,禁止选秒的配置
前端·javascript·vue.js·elementui·时间选选择器·样式修改
夏天想2 小时前
vue2+elementui使用compressorjs压缩上传的图片
前端·javascript·elementui
The_cute_cat2 小时前
JavaScript的初步学习
开发语言·javascript·学习
海天胜景2 小时前
vue3 el-table 列增加 自定义排序逻辑
javascript·vue.js·elementui
烛阴2 小时前
XPath 进阶:掌握高级选择器与路径表达式
前端·javascript