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

基于工作日与节假日的任务结束时间计算类优化求助

可配置任务结束时间计算类ScheduleCalculator分享与优化求助

我编写了一个可配置的基础类ScheduleCalculator,可结合工作日、工作时段、周末及公共节假日计算任务的结束时间。该类支持独立定义节假日、修改每日工作时段,经测试功能符合预期,但我认为存在更优雅的实现方式,特此分享代码寻求优化建议,同时帮助有同类需求的开发者。

using System.Globalization;

namespace akerdis.Scheduler
{
    public class WorkTime
    {
        public TimeSpan FromTime { get; set; }
        public TimeSpan ToTime { get; set; }


        public WorkTime(TimeSpan from, TimeSpan to)
        {
            FromTime = from;
            ToTime = to;
        }


        public bool isWorkTime(TimeSpan time)
        {
            TimeSpan timeCore = default(TimeSpan).Add(time);
            return (timeCore >= FromTime) && (timeCore <= ToTime);

        }


    }
    public class WorkDay
    {

        public DayOfWeek Day { get; set; }
        public bool isNotWorkDay { get; set; } = false;
        public IList<WorkTime>? WorkTimes { get; set; }

        public WorkDay(DayOfWeek day, bool notWorkDay = false)
        {

            Day = day;

            isNotWorkDay = notWorkDay;

            if (notWorkDay == false)
            {
                WorkTimes = new List<WorkTime>

                {
                    new WorkTime(new TimeSpan(8, 0, 0), new TimeSpan(12, 0, 0)),
                    new WorkTime(new TimeSpan(13, 0, 0), new TimeSpan(16, 0, 0))
                };

            }


        }

        public bool isWorkingDay { get { return !isNotWorkDay; } }


    }
    public class Holiday
    {
        public DateTime HolidayDate { get; set; }
        public string? HolidayName { get; set; }
        public int NumberOfDays { get; set; } = 1;


        public Holiday(DateTime date)
        {
            this.HolidayDate = date;
        }

        public Holiday(DateTime date, string name)
        {
            this.HolidayDate = date;
            this.HolidayName = name;
        }


        public Holiday(DateTime date, string name, int number)

        {
            this.HolidayDate = date;
            this.HolidayName = name;
            this.NumberOfDays = number;
        }


    }


    /// <summary>
    /// Class <c>ScheduleCalculator</c> utility class that allows us calculating the finish date and time 
    /// of a working task.
    /// /// <para>
    /// the calculation is based on the start date and  duration and takes into account
    /// of public holidays, business working days 
    /// </para>
    /// </summary>
    /// <author>
    /// AIT YAHIA Idir
    /// </author>
    /// <date>
    /// 02/27/2023
    /// </date>
    public class ScheduleCalculator

    {

        protected Dictionary<DayOfWeek, WorkDay> WorkDayOfWeeks = new Dictionary<DayOfWeek, WorkDay>()

        {
            { DayOfWeek.Sunday, new WorkDay(DayOfWeek.Sunday) },
            { DayOfWeek.Monday, new WorkDay(DayOfWeek.Monday) },
            { DayOfWeek.Tuesday, new WorkDay(DayOfWeek.Tuesday) },
            { DayOfWeek.Wednesday, new WorkDay(DayOfWeek.Wednesday)},
            { DayOfWeek.Thursday, new WorkDay(DayOfWeek.Thursday) },
            { DayOfWeek.Friday, new WorkDay(DayOfWeek.Friday,true) },
            { DayOfWeek.Saturday, new WorkDay(DayOfWeek.Saturday,true) },
         };



        public Holiday[] Holidays { get; set; } = { new Holiday(new(2023, 1, 1), "New year"), new Holiday(new DateTime(2023, 2, 26), "Jour de test", 2) };

        public bool isHoliday(DateTime date)
        {
            return Array.Exists(Holidays, holiday =>
            {
                if ((date >= holiday.HolidayDate) && (date <= holiday.HolidayDate.AddDays(holiday.NumberOfDays - 1)))
                { return true; }
                return false;
            });
        }

        public static bool IsValidTimeFormat(string input)
        {
            TimeSpan dummyOutput;
            return TimeSpan.TryParse(input, out dummyOutput);
        }

        public static bool IsValidDateTimeFormat(string input)
        {
            DateTime dummyOutput;
            return DateTime.TryParse(input, out dummyOutput);
        }

        public DateTime CalcFinishDate(string startDate, string duration)

        {


            DateTime _startDate = DateTime.ParseExact(startDate, "dd/MM/yyyy HH:mm",
                                       CultureInfo.InvariantCulture);
            TimeSpan _duration = default(TimeSpan).Add(TimeSpan.ParseExact(duration, "hh\:mm", CultureInfo.InvariantCulture)); ;
            return CalcFinishDate(_startDate, _duration);
        }
        public DateTime CalcFinishDate(DateTime startDate, TimeSpan duration)

        {
            if (duration == TimeSpan.Zero) { return startDate; }



            TimeSpan remainingDuration = duration;
            DateTime start_date = startDate;
            DateTime end_date = startDate + remainingDuration;
            DateTime nextDay = startDate.Date;
            TimeSpan timeToReduce = TimeSpan.Zero;



            while (remainingDuration > TimeSpan.Zero)

            {
                DayOfWeek day = nextDay.DayOfWeek;
                WorkDay wd = WorkDayOfWeeks[day];

                if ((wd.isWorkingDay) && (!isHoliday(nextDay)))

                {

                    foreach (WorkTime wt in wd.WorkTimes)

                    {
                        if (remainingDuration > TimeSpan.Zero)
                        {
                            DateTime period_start_date = nextDay.Date.Add(wt.FromTime);
                            DateTime period_end_date = nextDay.Date.Add(wt.ToTime);

                            if (start_date <= period_start_date)
                            {

                                start_date = period_start_date;
                                end_date = start_date.Add(remainingDuration);

                                if (end_date < period_start_date)
                                {
                                    timeToReduce = TimeSpan.Zero;
                                }
                                else
                                if ((end_date >= period_start_date) && (end_date < period_end_date))
                                {
                                    timeToReduce = end_date.Subtract(period_start_date);

                                }
                                else
                                if (end_date > period_end_date)
                                {
                                    end_date = period_end_date;
                                    timeToReduce = end_date.Subtract(start_date);

                                }
                            }

                            else if (start_date > period_start_date)
                            {
                                if (start_date > period_end_date)
                                {
                                    timeToReduce = TimeSpan.Zero;
                                }
                                else
                                    if ((start_date < end_date) && (end_date > period_end_date))
                                {
                                    end_date = period_end_date;
                                    timeToReduce = end_date.Subtract(start_date);

                                }
                                else if (end_date < period_end_date)
                                {
                                    timeToReduce = end_date.Subtract(start_date);

                                }
                            }


                            remainingDuration = remainingDuration.Subtract(timeToReduce);

                        }
                    }
                }

                nextDay = nextDay.AddDays(1);

            }


            return end_date;

        }


    }

}

使用示例

DateTime? date = scheduler.CalcFinishDate(new DateTime(2023, 02, 23, 16, 00, 00), new TimeSpan(08, 15, 0));

或

DateTime? date = scheduler.CalcFinishDate("27/02/2023 09:36","05:30");

内容的提问来源于stack exchange,提问作者Ait Yahia Idir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 10:18:14