4 Stimmen

Berechnen eines Datums nach Arbeitstagen/Stunden?

Ich arbeite derzeit an einer Website zur Verfolgung von Projekten. Darin ist es möglich, Service Level Agreements (SLAs) zu erstellen. Diese können mit den Wochentagen konfiguriert werden, an denen ein Projekt bearbeitet werden kann, und auch mit der Zeitspanne an jedem dieser Tage. Am Montag könnte dies z. B. zwischen 08:00 und 16:00 Uhr sein und am Freitag von 10:00 bis 14:00 Uhr. Sie werden auch mit einer Frist konfiguriert, die von der Priorität abhängt. So hat z. B. ein mit der Priorität "Niedrig" erstelltes Projekt eine Frist von zwei Wochen und ein Projekt mit der Priorität "Hoch" eine Frist von vier Stunden.

Das Problem, das ich habe, ist die Berechnung der Frist RUND um die oben beschriebenen Stunden. Angenommen, ich erstelle am Montag um 14:00 Uhr ein Projekt mit der Priorität "Hoch". Das heißt, ich habe vier Stunden für dieses Projekt. Aber wegen der Arbeitszeiten habe ich zwei Stunden am Montag (bis 16:00 Uhr) und dann weitere zwei Stunden am Freitag. Das bedeutet, dass der Abgabetermin für Freitag um 12:00 Uhr angesetzt werden muss.

Ich habe einige Zeit damit verbracht, dies zu googeln, und ich kann eine ganze Reihe von Beispielen finden, um herauszufinden, wie viele Arbeitsstunden zwischen einem bestimmten Start- und Enddatum liegen. Ich kann nur nicht herausfinden, wie man es in FINDING der Endzeit umwandelt, wenn man eine Startzeit und eine Zeitspanne bis zur Deadline angibt.

Die Tage/Zeitspannen werden in einer Sql-Datenbank in dem Format gespeichert:

Tag (z. B. 1 für Montag) StartHour EndHour

Die StartHour/EndHour werden als DateTimes gespeichert, aber natürlich ist nur der Zeitteil wichtig.

Die Art und Weise, die ich denke, es ist, ich habe irgendwie durch diese Zeiten zu iterieren und einige Datetime-Berechnungen zu tun. Ich kann nur nicht ganz herausfinden, was diese Berechnungen sein sollte, was der beste Weg ist.

Ich fand diese Frage hier auf der Website, als ich dies schrieb. Es ist in etwa das, was ich will, und ich spiele gerade damit, aber ich weiß immer noch nicht genau, wie ich es mit meinen dynamischen Arbeitstagen/-zeiten vereinbaren soll.

2voto

Esteban Brenes Punkte 1133

Hier ist etwas C#-Code, der helfen könnte, es könnte viel sauberer sein, aber es ist ein schneller erster Entwurf.

    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;
    }

1voto

Ian Jacobs Punkte 5396

Es gibt eine rekursive Lösung, die funktionieren könnte, versuchen Sie es so:

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

Dies ist natürlich nur ein grober Pseudocode. Hoffentlich gibt es Ihnen nur eine andere Möglichkeit, über das Problem nachzudenken.

1voto

Dan Goldstein Punkte 22942

Ich würde es folgendermaßen machen. Der Algorithmus prüft, ob das Problem heute abgeschlossen werden kann, und wenn nicht, wird die gesamte heutige Zeit genutzt, um die verbleibende Zeit des Problems zu reduzieren und auf morgen zu verschieben.

  1. Finden Sie die Zeit, die Sie haben, um das Problem als TimeSpan zu schließen (ich nenne dies die verbleibende Zeit des Problems)
  2. Erstellen Sie für jeden Arbeitstag eine DateTime, die nur die Uhrzeit des Beginns und des Endes enthält.
  3. Setzen Sie die Startzeit auf jetzt.
  4. Schleife:
    1. Ermitteln Sie die verbleibende Zeit des heutigen Tages, indem Sie die heutige Endzeit minus die Startzeit subtrahieren (das Ergebnis sollte ein TimeSpan sein)
    2. Wenn die heutige Restzeit größer ist als die Restzeit der Ausgabe, nehmen Sie das heutige Datum und die heutige Startzeit + Restzeit der Ausgabe
    3. Wenn die verbleibende Zeit der Ausgabe größer ist, setzen Sie die verbleibende Zeit der Ausgabe auf die verbleibende Zeit der Ausgabe minus die verbleibende Zeit von heute, gehen Sie zu morgen und gehen Sie an den Anfang der Schleife.

1voto

Loscas Punkte 551

Mit Stu's respuesta Ändern Sie als Ausgangspunkt die Funktion IsInBusinessHours, um die Geschäftszeiten für den Datumsparameter zu ermitteln. Ein Verfahren wie das folgende könnte verwendet werden:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X