You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Outlook Graph API的重复日程生成逻辑问题排查

重复日程生成代码的逻辑错误分析

从Microsoft Outlook Graph API获取重复日程数据时,生成重复日期的代码在部分场景下完全失效,比如给定每周一至周五、时间范围2023-10-26至2023-11-10的重复模式。以下是对代码逻辑错误的分析:

重复模式示例

{
  "recurrence": {
    "pattern": {
      "type": "weekly",
      "interval": 1,
      "month": 0,
      "dayOfMonth": 0,
      "daysOfWeek": ["monday", "tuesday", "wednesday", "thursday", "friday"],
      "firstDayOfWeek": "sunday",
      "index": "first"
    },
    "range": {
      "type": "endDate",
      "startDate": "2023-10-26T00:00:00.000Z",
      "endDate": "2023-11-10T23:59:00.000Z",
      "recurrenceTimeZone": "Romance Standard Time",
      "numberOfOccurrences": 0
    }
  }
}

代码实现

主函数:generateRecurringDates

export const generateRecurringDates = (appointment, end_date, start_date) => {
    const { pattern, range } = appointment.recurrence;
    const startDate = new Date(range.startDate);
    let endDate;
    let endTime;
    if(range.type == 'noEnd'){
        endDate = new Date(end_date);
        endTime ='23:59:00.000Z'
    }else{
        endDate = new Date(range.endDate);
        endTime =endDate.toISOString().split('T')[1]
    }
    const recurringDates = [];
    let currentDate = startDate;
    let startTime =currentDate.toISOString().split('T')[1]
    let end;
    if(endDate >=  new Date(end_date)){
        end = new Date(end_date)
    }else{
        end = endDate
    }
    let start;
    let added = true;
    if(startDate >=  new Date(start_date)){
        currentDate = start = startDate
    }else{
        added = false
        currentDate = start = new Date(start_date)
    }
    while (currentDate <= end) {
      if(pattern?.daysOfWeek?.length > 0){
                pattern?.daysOfWeek?.map((dayName) =>
                {
                    var days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
                    if(pattern.type == 'relativeMonthly'){
                        recurringDates.push({
                            start: currentDate.toISOString().split('T')[0],
                            end: currentDate.toISOString().split('T')[0],
                            title: appointment.title
                        });
                    }else{
                        if(pattern?.daysOfWeek?.includes(days[currentDate.getDay()].toString()) && currentDate <= end){
                            recurringDates.push({
                                start: currentDate.toISOString().split('T')[0],
                                end: currentDate.toISOString().split('T')[0],
                                title: appointment.title
                            });
                        }
                    }
                    currentDate = adjustDate(currentDate, pattern,dayName);
                }
            );
      }else{
            if(currentDate <= end){
                if(pattern.dayOfMonth > 0){
                    if(pattern.month > 0 && (pattern.month == (currentDate.getMonth()+1)) && pattern.dayOfMonth == currentDate.getDate()){
                        recurringDates.push({
                            start: currentDate.toISOString().split('T')[0],
                            end: currentDate.toISOString().split('T')[0]
                        });
                    }else if(pattern.month == 0 && pattern.dayOfMonth == currentDate.getDate()){
                        recurringDates.push({
                            start: currentDate.toISOString().split('T')[0],
                            end: currentDate.toISOString().split('T')[0]
                        });
                    }
                }else{
                    recurringDates.push({
                        start: currentDate.toISOString().split('T')[0],
                        end: currentDate.toISOString().split('T')[0]
                    });
                }
            }
            currentDate = adjustDate(currentDate, pattern);
      }
    }
  
    return recurringDates;
  }

辅助函数:adjustDate

function adjustDate(date, pattern, dayName ='') {
    let newDate = new Date(date);
  
    switch (pattern.type) {
      case "daily":
        newDate.setDate(newDate.getDate() + pattern.interval);
        break;
  
      case "weekly":
        if (pattern.interval == 1) {
            let days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
        }

        if (dayName != '') {
            var days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
            var day = days.indexOf(dayName.toLowerCase());
            let addDay = ((day + 7 - newDate.getDay()) % 7);
            if(addDay == 0){
                newDate.setDate(newDate.getDate() +1)
            }else{
                newDate.setDate(newDate.getDate() + (((day + 7 - newDate.getDay()) % 7) || 7))
            }
        } else {
            newDate.setDate(newDate.getDate() + pattern.interval * 7);
        }
        break;
  
      case "absoluteMonthly":
        if (pattern.dayOfMonth) {
          const currentMonth = newDate.getMonth();
          const targetDayOfMonth = pattern.dayOfMonth;
  
          if (targetDayOfMonth > newDate.getDate()) {
            newDate.setDate(targetDayOfMonth);
          } else {
            newDate.setMonth(currentMonth + 1);
            newDate.setDate(targetDayOfMonth);
          }
        } else {
          newDate.setMonth(newDate.getMonth() + pattern.interval);
        }
        break;
        
    case "relativeMonthly":
        if (pattern.daysOfWeek && pattern.daysOfWeek.length > 0) {
            var days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
            var day = days.indexOf(dayName.toLowerCase());
            var nextOccurrence = findNthDayOfMonth(newDate.getFullYear(), newDate.getMonth(), day, pattern.index);
            newDate = new Date(nextOccurrence);
        } else {
            newDate.setMonth(newDate.getMonth() + pattern.interval);
        }
        break;

      case "absoluteYearly":
        if (pattern.dayOfMonth > 0) {
            const targetDate = new Date(newDate);
            targetDate.setMonth(pattern.month - 1);
            targetDate.setDate(pattern.dayOfMonth);
    
            if (targetDate <= newDate) {
              newDate.setFullYear(newDate.getFullYear() + pattern.interval);
              newDate.setMonth(pattern.month - 1);
              newDate.setDate(pattern.dayOfMonth);
            } else {
              newDate.setMonth(pattern.month - 1);
              newDate.setDate(pattern.dayOfMonth);
            }
          } else {
            newDate.setFullYear(newDate.getFullYear() + pattern.interval);
            newDate.setMonth(pattern.month - 1);
            newDate.setDate(1);
    
            while (newDate.getDay() !== 0) {
              newDate.setDate(newDate.getDate() + 1);
            }
    
            newDate.setDate(newDate.getDate() + (pattern.interval - 1) * 7);
          }
        break;
          
    case "relativeYearly":
        if (pattern.daysOfWeek && pattern.daysOfWeek.length > 0) {
            var days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
            var day = days.indexOf(dayName.toLowerCase());
            var nextOccurrence = findNthDayOfMonth(newDate.getFullYear() + pattern.interval, pattern.month - 1, day, pattern.index);
            newDate = new Date(nextOccurrence);
        } else {
            newDate.setFullYear(newDate.getFullYear() + pattern.interval);
        }
        break;
    
      default:
        break;
    }
  
    return newDate;
  }

逻辑错误分析

1. 每周重复模式的日期调整逻辑错误

在adjustDate的weekly分支中,当传入dayName时,日期计算逻辑存在致命问题:

  • 当currentDate已经是目标星期几(即addDay === 0)时,代码会强制加1天,直接跳过当前符合条件的日期;
  • 计算addDay的冗余逻辑(((day + 7 - newDate.getDay()) % 7) || 7)完全多余,因为取模结果已经是0-6的有效值,无需额外处理。

2. while循环与map遍历的逻辑冲突

在generateRecurringDates的while循环内,对pattern.daysOfWeek使用map遍历:

  • 每次遍历都会调用adjustDate修改currentDate,导致currentDate在一次while循环中连续跳转到5个不同的日期(周一到周五);
  • while循环的条件是currentDate <= end,后续循环会直接处理已经跳过的日期范围,导致大量日期遗漏或重复生成。

3. 日期范围处理的冗余与错误

  • added变量定义后未被使用,属于无效代码;
  • 当startDate < start_date时,直接将currentDate设为start_date,但未检查该日期是否属于daysOfWeek指定的星期,会生成不符合要求的日期;
  • 完全忽略了range.recurrenceTimeZone时区参数,直接使用本地时间解析日期,导致时区转换错误,生成的日期与Outlook中的实际日程不符。

4. 重复日期添加的条件判断错误

在map遍历daysOfWeek时,判断条件pattern?.daysOfWeek?.includes(days[currentDate.getDay()].toString())完全多余:

  • 因为当前遍历的dayName本身就是daysOfWeek中的元素,且adjustDate已经将currentDate调整为目标星期几;
  • 该判断会导致当currentDate因调整逻辑出错时,错误跳过日期添加,进一步加剧问题。

5. 未处理firstDayOfWeek参数

Outlook重复模式中的firstDayOfWeek字段定义了每周的起始日,但代码完全未考虑该参数:

  • 比如当firstDayOfWeek为sunday时,每周的重复周期应从周日开始计算,但代码的星期索引逻辑未结合该参数调整,导致跨周的重复日程计算错误。

内容的提问来源于stack exchange,提问作者COdeingNinja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 13:19:50