js根据给出的月份设置日期

最近项目的需求是这样的,有开始日期和结束日期的限制,同时根据一个首次约定日期以及一个周期(月、季度、半年、一年),自动生成一条或者多条数据的列表,其中列表的约定日期规则如下:
① 周期为月:根据首次约定日期,每月固定日期生成一条数据
② 周期为季度:根据首次约定日期,每三个月固定日期生成一条数据
③ 周期为半年:根据首次约定日期,每六个月固定日期生成一条数据
④ 周期为年:根据首次约定日期,每年固定日期生成一条数据
⑤ 特殊情况:当首次约定日期设置为29/30/31日时,当某月份没有29、30或者31日时,则取该月份最后一天为列表的约定日期

在做这个需求的时候,遇到过一些坑,现在记录一下,给自己加深印象也给各位大佬看看,欢迎提出问题一起探讨~

/**
 * 计算生成表格的条数
 */
calcTableData() {
  // this.startDate, this.endDate: 限制的开始和结束日期
  // this.period: 周期(月/季度/半年/年), this.appointDate: 首次约定日期
  if (this.startDate && this.endDate && this.period && this.appointDate) {
    let dateStart = moment(this.startDate).format("YYYY/MM/DD");
    let dateEnd = moment(this.endDate).format("YYYY/MM/DD");

    // 计算开始日期和结束日期相差几个月
    const dateMinus = (d1: any, d2: any) => {
      let m1 = parseInt(d1.split("/")[1].replace(/^0+/, "")) + parseInt(d1.split("/")[0]) * 12;
      let m2 = parseInt(d2.split("/")[1].replace(/^0+/, "")) + parseInt(d2.split("/")[0]) * 12;
      let dateDiff = 0;
      // 当结束的日期比开始日期的日期(几号)大时, 月份+1
      if (new Date(dateEnd).getDate() > new Date(dateStart).getDate()) {
        dateDiff = 1;
      }
      let diff = m2 - m1 + dateDiff;
      return diff;
    }
    const diff = dateMinus(dateStart, dateEnd);
    // 此处为方便计算, this.period按月份基数来, 即周期为月时period为1个月, 周期为季度时period为3个月, 周期为半年时period为6个月...
    switch (this.period) {
      // 月
      case '1':
        this.handleTableData(diff, this.period);
        break;
      // 季度
      case '3':
        this.handleTableData(Math.ceil(diff / 3), this.period);
        break;
      // 半年
      case '6':
        this.handleTableData(Math.ceil(diff / 6), this.period);
        break;
      // 一年
      default:
        this.handleTableData(Math.ceil(diff / 12), this.period);
    }
  }
}
handleTableData(count: number, type: string) {
  this.tableData = [];
  for (let i = 0; i < count; i++) {
    let obj = {
      '其他属性': '',
      // 当列表数据为第一条时, 直接取首次约定日期
      appointDate: i === 0 ? this.appointDate : this.hanldeDate(i, type),
      key: i,
    }
    this.tableData.push(obj);
  }
}
关键日期算法及思路:

这里取28号是因为平年时, 2月份最多28天。

hanldeDate(i: number, type: string) {
  // baseDate为列表上一条数据的约定日期
  let baseDate = new Date(this.tableData[i - 1].planDate);
  let appointDate = baseDate.setMonth(baseDate.getMonth() + Number(type)) as any;
  appointDate = moment(appointDate);
  /**
   * 首先获取首次约定日期的日期 day是几号
   * 如果day小于等于28, 直接setMonth的参数为baseDate的月份即可。
   * 如果day大于28的情况下:
   *   计算appointDate减去5天, 求出appointDate这个月的最后一天是几号为endDate, 
   *   如果appointDate这个月的最后一天大于day, 那么setMonth只需设置月份
   *   如果appointDate这个月的最后一天小于day, 那么setMonth需要设置2个参数, 参数为:月份和该月最后一天的日期endDate
   * 例:
   *   如果首次约定日期为2021/8/28, 求出day为28, 此时setMonth(baseDate.getMonth())即可
   *   如果首次约定日期为2021/8/31, 求出day为31, 当周期为按月时, 直接setMonth(baseDate.getMonth())会有bug, 因为9月没有30号, 此时appointDate会变成10月1号,
   *   因此可将appointDate减去5天, 再计算出9月的最后一天endDate为30号, 由于endDate小于day, appointDate的结果为setMonth(baseDate.getMonth(), endDate)。
   */
  let day = (new Date(moment(this.appointDate).format("YYYY-MM-DD"))).getDate();
  if (day > 28) {
    let dateCopy = (new Date(appointDate)).getTime() - 5 * 24 * 60 * 60 * 1000;
    let endDateCopy = getEndDate(new Date(dateCopy)) as any;
    let endDate = new Date(endDateCopy.endDate).getDate();
    if (endDate < day) {
      // 此处需要减1, 因为baseDate经过上面的设置后, 此时的baseDate已经为下一个月的1号了
      appointDate = baseDate.setMonth(baseDate.getMonth() - 1, endDate) as any;
    } else {
      appointDate = baseDate.setMonth(baseDate.getMonth(), day) as any;
    }
    appointDate = moment(appointDate)
  }
  return appointDate;
}

getEndDate函数在此,哈哈
getEndDate函数来源于网络… 不好意思,实在是忘记出处了。

export function getEndDate(date: any) {
  let nowDate = date;
  let cloneNowDate = date;
  let fullYear = nowDate.getFullYear();
  let month = nowDate.getMonth() + 1; // getMonth 方法返回 0-11,代表1-12月
  let endOfMonth = new Date(fullYear, month, 0).getDate(); // 获取本月最后一天
  function getFullDate(targetDate: any) {
    let D, y, m, d;
    if (targetDate) {
      D = new Date(targetDate);
      y = D.getFullYear();
      m = D.getMonth() + 1;
      d = D.getDate();
    } else {
      y = fullYear;
      m = month;
      d = date;
    }
    m = m > 9 ? m : '0' + m;
    d = d > 9 ? d : '0' + d;
    return y + '-' + m + '-' + d;
  }
  let endDate = getFullDate(cloneNowDate.setDate(endOfMonth));//当月最后一天
  let starDate = getFullDate(cloneNowDate.setDate(1));//当月第一天
  return { endDate, starDate }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值