【问题标题】:Calculating a date around working days/hours?计算工作日/小时左右的日期?
【发布时间】:2008-10-10 17:01:06
【问题描述】:

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

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

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

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

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

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

按照我的理解,我必须以某种方式遍历这些时间并进行一些日期时间计算。我只是不太清楚这些计算应该是什么,最好的方法是什么。

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

【问题讨论】:

    标签: c# date


    【解决方案1】:

    这里有一些 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;
        }
    

    【讨论】:

      【解决方案2】:

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

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

      显然这是相当粗糙的伪代码。希望它只是为您提供另一种思考问题的方式。

      【讨论】:

        【解决方案3】:

        我会这样做。算法是看今天能不能关闭issue,如果不能关闭,就用今天的所有时间减少issue的剩余时间,到明天去。

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

        【讨论】:

          【解决方案4】:

          使用 Stu 的 answer 作为起点,修改 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
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2023-03-19
            • 1970-01-01
            • 2016-07-23
            • 1970-01-01
            • 1970-01-01
            • 2010-09-20
            相关资源
            最近更新 更多