Вычисление даты вокруг рабочих дней / часов? - PullRequest
4 голосов
/ 10 октября 2008

Я сейчас работаю над сайтом для отслеживания проектов. В нем можно создавать соглашения об уровне обслуживания (SLA). Они настраиваются с учетом дней недели, в течение которых можно работать над проектом, а также времени в каждый из этих дней. Например. в понедельник это может быть с 08:00 до 16:00, а затем в пятницу с 10:00 до 14:00. Они также настроены с крайним сроком в зависимости от приоритета. Например. проект, созданный с «низким» приоритетом, имеет крайний срок в две недели, а проект с «высоким» приоритетом имеет срок в четыре часа.

Проблема, с которой я столкнулся, заключается в расчете срока примерно в часы, описанные ранее. Скажем, я создаю проект в понедельник в 14:00 с «высоким» приоритетом. Это означает, что у меня есть четыре часа для этого проекта. Но из-за рабочего времени у меня есть два часа в понедельник (до 16:00), а затем еще два часа в пятницу. Это означает, что крайний срок должен быть установлен на пятницу в 12:00.

Я потратил довольно много времени на поиски в Интернете, и я могу найти немало примеров, чтобы узнать, сколько рабочих часов существует между заданной датой начала и окончанием. Я просто не могу понять, как преобразовать его в НАЙТИ дату окончания, учитывая время начала и количество времени до крайнего срока.

Дневные / временные интервалы хранятся в базе данных sql в формате:

День (например, 1 для понедельника) StartHour EndHour

StartHour / EndHour сохраняются как DateTimes, но, конечно, важна только часть времени.

Как я понимаю, мне нужно как-то перебирать эти времена и делать некоторые расчеты даты и времени. Я просто не могу понять, какими должны быть эти расчеты, как лучше.

Я нашел этот вопрос здесь на сайте, когда я писал это. Это своего рода то, чего я хочу, и я играю с этим прямо сейчас, но я все еще теряюсь в том, как именно заставить это работать вокруг моих динамичных рабочих дней / часов.

Ответы [ 4 ]

2 голосов
/ 10 октября 2008

Вот некоторый код 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;
    }
1 голос
/ 10 октября 2008

Вот как бы я это сделал. Алгоритм состоит в том, чтобы увидеть, можно ли закрыть проблему сегодня и, если нет, использовать все сегодняшнее время, чтобы сократить оставшееся время проблемы и перейти к завтрашнему дню.

  1. Найдите время, которое вам нужно, чтобы закрыть проблему как интервал времени (я называю это оставшееся время проблемы)
  2. Для каждого рабочего дня создайте DateTime, в котором есть только время начала и окончания.
  3. Установите время начала сейчас.
  4. Loop:
    1. Найдите текущее оставшееся время, вычтя сегодняшнее конечное время минус время начала (результат должен быть TimeSpan)
    2. Если оставшееся сегодня время больше, чем оставшееся время выпуска, возьмите сегодняшнюю дату и сегодняшнее время начала + оставшееся время выпуска
    3. Если оставшееся время проблемы больше, установите оставшееся время проблемы равным оставшемуся времени проблемы минус оставшееся сегодня время, перейдите к завтра и перейдите к началу цикла.
1 голос
/ 10 октября 2008

Используя 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
1 голос
/ 10 октября 2008

Существует рекурсивное решение, которое может сработать, попробуйте подумать так:

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

Очевидно, это довольно грубый псевдокод. Надеюсь, это просто даст вам другой способ думать о проблеме.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...