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

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

相关推荐
Mr Xu_7 小时前
告别冗长 switch-case:Vue 项目中基于映射表的优雅路由数据匹配方案
前端·javascript·vue.js
前端摸鱼匠7 小时前
Vue 3 的toRefs保持响应性:讲解toRefs在解构响应式对象时的作用
前端·javascript·vue.js·前端框架·ecmascript
sleeppingfrog7 小时前
zebra通过zpl语言实现中文打印(二)
javascript
未来之窗软件服务8 小时前
未来之窗昭和仙君(六十五)Vue与跨地区多部门开发—东方仙盟练气
前端·javascript·vue.js·仙盟创梦ide·东方仙盟·昭和仙君
baidu_247438618 小时前
Android ViewModel定时任务
android·开发语言·javascript
VT.馒头9 小时前
【力扣】2721. 并行执行异步函数
前端·javascript·算法·leetcode·typescript
有位神秘人9 小时前
Android中Notification的使用详解
android·java·javascript
phltxy10 小时前
Vue 核心特性实战指南:指令、样式绑定、计算属性与侦听器
前端·javascript·vue.js
Byron070711 小时前
Vue 中使用 Tiptap 富文本编辑器的完整指南
前端·javascript·vue.js
Mr Xu_12 小时前
告别硬编码:前端项目中配置驱动的实战优化指南
前端·javascript·数据结构