4

我目前正在一个网站上跟踪项目。在其中,可以创建服务水平协议 (SLA)。这些可配置为项目可以在一周中的哪几天进行,以及每一天的时间跨度。例如。周一可能是 08:00 到 16:00 之间,然后是周五 10:00 到 14:00。它们还根据优先级配置了截止时间。例如。以“低”优先级创建的项目的截止时间为两周,而“高”优先级的项目的截止时间为四个小时。

我遇到的问题是计算前面描述的几个小时左右的截止日期。假设我在星期一 14:00 创建了一个具有“高”优先级的项目。这意味着我有四个小时的时间来完成这个项目。但由于工作时间的关系,我周一有两个小时(直到 16:00),周五还有两个小时。这意味着截止日期必须设置为星期五 12:00。

我花了很多时间在谷歌上搜索,我可以找到很多例子来找出给定的开始结束日期之间有多少工作时间。我只是不知道如何将其转换为 FINDING 结束日期时间,给定开始时间和截止日期前的时间量。

日期/时间跨度以以下格式存储在 sql 数据库中:

日期(例如 1 表示星期一) StartHour EndHour

StartHour/EndHour 保存为 DateTimes,但当然只有时间部分很重要。

我认为它的方式是,我必须以某种方式遍历这些时间并进行一些日期时间计算。我只是无法弄清楚这些计算应该是什么,最好的方法是什么。

我在写这篇文章时在网站上发现了这个问题。这是我想要的,我现在正在玩它,但我仍然不知道如何让它在我动态的工作日/小时内工作。

4

4 回答 4

2

这里有一些 C# 代码可能会有所帮助,它可能会更简洁,但它是一个快速的初稿。

    class Program
    {
        static void Main(string[] args)
        {
            // Test
            DateTime deadline = DeadlineManager.CalculateDeadline(DateTime.Now, new TimeSpan(4, 0, 0));
            Console.WriteLine(deadline);
            Console.ReadLine();
        }
    }

    static class DeadlineManager
    {
        public static DateTime CalculateDeadline(DateTime start, TimeSpan workhours)
        {
            DateTime current = new DateTime(start.Year, start.Month, start.Day, start.Hour, start.Minute, 0);
            while(workhours.TotalMinutes > 0)
            {
                DayOfWeek dayOfWeek = current.DayOfWeek;
                Workday workday = Workday.GetWorkday(dayOfWeek);
                if(workday == null)
                {
                    DayOfWeek original = dayOfWeek;
                    while (workday == null)
                    {
                        current = current.AddDays(1);
                        dayOfWeek = current.DayOfWeek;
                        workday = Workday.GetWorkday(dayOfWeek);
                        if (dayOfWeek == original)
                        {
                            throw new InvalidOperationException("no work days");
                        }
                    }
                    current = current.AddHours(workday.startTime.Hour - current.Hour);
                    current = current.AddMinutes(workday.startTime.Minute - current.Minute);
                }

                TimeSpan worked = Workday.WorkHours(workday, current);
                if (workhours > worked)
                {
                    workhours = workhours - worked;
                    // Add one day and reset hour/minutes
                    current = current.Add(new TimeSpan(1, current.Hour * -1, current.Minute * -1, 0));
                }
                else
                {
                    current.Add(workhours);
                    return current;
                }
            }
            return DateTime.MinValue;
        }
    }

    class Workday
    {
        private static readonly Dictionary<DayOfWeek, Workday> Workdays = new Dictionary<DayOfWeek, Workday>(7);
        static Workday()
        {
            Workdays.Add(DayOfWeek.Monday, new Workday(DayOfWeek.Monday, new DateTime(1, 1, 1, 10, 0, 0), new DateTime(1, 1, 1, 16, 0, 0)));
            Workdays.Add(DayOfWeek.Tuesday, new Workday(DayOfWeek.Tuesday, new DateTime(1, 1, 1, 10, 0, 0), new DateTime(1, 1, 1, 16, 0, 0)));
            Workdays.Add(DayOfWeek.Wednesday, new Workday(DayOfWeek.Wednesday, new DateTime(1, 1, 1, 10, 0, 0), new DateTime(1, 1, 1, 16, 0, 0)));
            Workdays.Add(DayOfWeek.Thursday, new Workday(DayOfWeek.Thursday, new DateTime(1, 1, 1, 10, 0, 0), new DateTime(1, 1, 1, 16, 0, 0)));
            Workdays.Add(DayOfWeek.Friday, new Workday(DayOfWeek.Friday, new DateTime(1, 1, 1, 10, 0, 0), new DateTime(1, 1, 1, 14, 0, 0)));
        }

        public static Workday GetWorkday(DayOfWeek dayofWeek)
        {
            if (Workdays.ContainsKey(dayofWeek))
            {
                return Workdays[dayofWeek];
            }
            else return null;
        }

        public static TimeSpan WorkHours(Workday workday, DateTime time)
        {
            DateTime sTime = new DateTime(time.Year, time.Month, time.Day,
                workday.startTime.Hour, workday.startTime.Millisecond, workday.startTime.Second);
            DateTime eTime = new DateTime(time.Year, time.Month, time.Day,
                workday.endTime.Hour, workday.endTime.Millisecond, workday.endTime.Second);
            if (sTime < time)
            {
                sTime = time;
            }
            TimeSpan span = eTime - sTime;
            return span;
        }

        public static DayOfWeek GetNextWeekday(DayOfWeek dayOfWeek)
        {
            int i = (dayOfWeek == DayOfWeek.Saturday) ? 0 : ((int)dayOfWeek) + 1;
            return (DayOfWeek)i;
        }


        private Workday(DayOfWeek dayOfWeek, DateTime start, DateTime end)
        {
            this.dayOfWeek = dayOfWeek;
            this.startTime = start;
            this.endTime = end;
        }

        public DayOfWeek dayOfWeek;
        public DateTime startTime;
        public DateTime endTime;
    }
于 2008-10-10T19:46:14.143 回答
1

有一个可行的递归解决方案,请尝试按照以下思路进行思考:

public DateTime getDeadline(SubmitTime, ProjectTimeAllowed)
{
   if (SubmitTime+ProjectTimeAllowed >= DayEndTime)
           return getDeadline(NextDayStart, ProjectTimeAllowed-DayEndTime-SubmitTime)
   else
           return SubmitTime + ProjectTimeAllowed
}

显然这是相当粗糙的伪代码。希望它只是给你另一种方式来思考这个问题。

于 2008-10-10T17:17:58.920 回答
1

这就是我将如何做到的。该算法是看今天是否可以关闭问题,如果不能,则使用今天的所有时间来减少问题的剩余时间并转到明天。

  1. 找出你必须以 TimeSpan 的形式关闭问题的时间(我称之为问题的剩余时间)
  2. 对于每个工作日,创建一个仅包含开始时间和结束时间的 DateTime。
  3. 将开始时间设置为现在。
  4. 环形:
    1. 通过减去今天的结束时间减去开始时间找到今天的剩余时间(结果应该是 TimeSpan)
    2. 如果今天的剩余时间大于问题的剩余时间,取今天的日期和今天的开始时间+问题的剩余时间
    3. 如果问题的剩余时间较长,则将问题的剩余时间设置为问题的剩余时间减去今天的剩余时间,移动到明天,然后转到循环顶部。
于 2008-10-10T19:35:09.500 回答
1

使用 Stu 的回答作为起点,修改 IsInBusinessHours 函数以查找日期参数的营业时间。可以使用如下程序:

CREATE PROCEDURE [dbo].[IsInBusinessHours]
    @MyDate DateTime 
AS
BEGIN
    SELECT     CASE Count(*) WHEN 0 THEN 0 ELSE 1 END AS IsBusinessHour
FROM         WorkHours
WHERE     (DATEPART(hour, StartHours) <= DATEPART(hour, @MyDate)) AND (DATEPART(hour, EndHours) > DATEPART(hour, @MyDate)) AND (Day = DATEPART(WEEKDAY, 
                      @MyDate))
END
于 2008-10-10T19:43:07.817 回答