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

如何基于自定义重复模式高效生成List<DateTime>类型的日期序列?

完整实现代码

public static class RepeatDateGenerator
{
    // 辅助方法:转换自定义DAY枚举为系统DayOfWeek
    private static DayOfWeek ConvertToSystemDayOfWeek(DAY day)
    {
        return day switch
        {
            DAY.Sunday => DayOfWeek.Sunday,
            DAY.Monday => DayOfWeek.Monday,
            DAY.Tuesday => DayOfWeek.Tuesday,
            DAY.Wednesday => DayOfWeek.Wednesday,
            DAY.Thursday => DayOfWeek.Thursday,
            DAY.Friday => DayOfWeek.Friday,
            DAY.Saturday => DayOfWeek.Saturday,
            _ => throw new ArgumentOutOfRangeException(nameof(day))
        };
    }

    // 辅助方法:获取指定年月的逻辑目标日期(比如第二个周六、最后一个周一)
    private static DateTime GetLogicalDate(int year, int month, LOGICALSTART start, DAY targetDay)
    {
        var sysTargetDay = ConvertToSystemDayOfWeek(targetDay);
        var firstDayOfMonth = new DateTime(year, month, 1);
        int daysToAdd = (sysTargetDay - firstDayOfMonth.DayOfWeek + 7) % 7;
        var firstMatchDay = firstDayOfMonth.AddDays(daysToAdd);

        return start switch
        {
            LOGICALSTART.First => firstMatchDay,
            LOGICALSTART.Second => firstMatchDay.AddDays(7),
            LOGICALSTART.Third => firstMatchDay.AddDays(14),
            LOGICALSTART.Fourth => firstMatchDay.AddDays(21),
            LOGICALSTART.Last => 
                // 取最后一个匹配的周几,先算下个月第一天,往前找
                firstDayOfMonth.AddMonths(1).AddDays(-1)
                    .AddDays(-((firstDayOfMonth.AddMonths(1).AddDays(-1).DayOfWeek - sysTargetDay + 7) % 7)),
            _ => throw new ArgumentOutOfRangeException(nameof(start))
        };
    }

    public static List<DateTime> GenerateUpcomingDates(this Repeat repeat, DateTime from, DateTime till)
    {
        List<DateTime> result = new List<DateTime>();
        var current = from.Date;

        switch (repeat.PatternMode)
        {
            case PATTERNMODE.Day:
                while (current <= till)
                {
                    result.Add(current);
                    current = current.AddDays(repeat.PatternValue);
                }
                break;

            case PATTERNMODE.Week:
                // 优化原有逻辑,不用每天遍历,按周跳
                var weekStep = repeat.PatternValue * 7;
                while (current <= till)
                {
                    var weekStart = current.AddDays(-(int)current.DayOfWeek);
                    for (int i = 0; i < 7; i++)
                    {
                        var day = weekStart.AddDays(i);
                        if (day < from) continue;
                        if (day > till) break;
                        bool isMatch = day.DayOfWeek switch
                        {
                            DayOfWeek.Sunday => repeat.Sunday,
                            DayOfWeek.Monday => repeat.Monday,
                            DayOfWeek.Tuesday => repeat.Tuesday,
                            DayOfWeek.Wednesday => repeat.Wednesday,
                            DayOfWeek.Thursday => repeat.Thursday,
                            DayOfWeek.Friday => repeat.Friday,
                            DayOfWeek.Saturday => repeat.Saturday,
                            _ => false
                        };
                        if (isMatch) result.Add(day);
                    }
                    current = current.AddDays(weekStep);
                }
                break;

            case PATTERNMODE.Month:
                while (current <= till)
                {
                    DateTime targetDate;
                    if (repeat.Constrain == CONSTRAIN.DayOfMonth)
                    {
                        // 处理当月没有指定DayOfMonth的情况,取当月最后一天
                        int daysInMonth = DateTime.DaysInMonth(current.Year, current.Month);
                        int targetDay = Math.Min(repeat.DayOfMonth, daysInMonth);
                        targetDate = new DateTime(current.Year, current.Month, targetDay);
                    }
                    else // Logical模式
                    {
                        targetDate = GetLogicalDate(current.Year, current.Month, repeat.LogicalStart, repeat.LogicalDay);
                    }

                    if (targetDate >= from && targetDate <= till)
                    {
                        result.Add(targetDate);
                    }
                    // 按PatternValue跳月,实现每N个月的逻辑
                    current = current.AddMonths(repeat.PatternValue);
                    // 对齐到月初避免跨月误差
                    current = new DateTime(current.Year, current.Month, 1);
                }
                break;

            case PATTERNMODE.Year:
                while (current <= till)
                {
                    DateTime targetDate;
                    if (repeat.Constrain == CONSTRAIN.DayOfMonth)
                    {
                        int daysInMonth = DateTime.DaysInMonth(current.Year, current.Month);
                        int targetDay = Math.Min(repeat.DayOfMonth, daysInMonth);
                        targetDate = new DateTime(current.Year, current.Month, targetDay);
                    }
                    else
                    {
                        targetDate = GetLogicalDate(current.Year, current.Month, repeat.LogicalStart, repeat.LogicalDay);
                    }

                    if (targetDate >= from && targetDate <= till)
                    {
                        result.Add(targetDate);
                    }
                    // 按PatternValue跳年
                    current = current.AddYears(repeat.PatternValue);
                    current = new DateTime(current.Year, current.Month, 1);
                }
                break;
        }

        // 去重+排序避免异常情况
        return result.Distinct().OrderBy(d => d).ToList();
    }
}

关键逻辑说明

  • 修复月模式按日生成的问题:原来的逻辑是每天遍历且没有按PatternValue跳月,修正后直接按月步长跳转,同时处理了小月没有指定日期的边界情况,比如2月没有31号时自动取当月最后一天。
  • 实现逻辑日期规则:通过GetLogicalDate辅助方法计算每月第N个周几、最后一个周几的日期,适配「每月第二个周六」这类需求。
  • 「每N个月/每年」逻辑:处理月/年模式时直接按PatternValue跳对应的时间单位,不需要每天遍历,执行效率提升明显。
  • 原有周模式优化:调整为按周步长遍历,减少循环次数。

可选效率提升方案(优先级低)

如果后续需要处理更复杂的重复规则,可以直接用成熟的Cron表达式解析库,比如NCrontab,直接把前端选择的规则转换为Cron表达式,再通过库生成符合要求的日期列表,不需要自行处理所有边界逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 17:36:04