Autor Beitrag
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10181
Erhaltene Danke: 1254

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Di 07.03.17 19:23 
Moin!

Komischer Titel, sorry, mir fällt kein besserer ein. :( Ich brauche bitte mal etwas Input, wie man dieses Problem grundsätzlich angeht. Es geht um folgendes:
  • Gegeben sind zwei Unix-Timestamps ("Zeitpunkte")
  • Die Differenz der Timestamps ist die "Laufzeit" in Sekunden, soweit, so einfach ;)
  • Leider gibt es aber pro Wochentag "gewertete" und "wertungsfreie" Intervalle (hört sich schwieriger an, als es ist): Beispiel, MO, MI und FR "zählt" nur die Zeit zwischen 8-16 Uhr, DI und DO zwischen 8-18 Uhr, die restliche Zeit des Tages (SA+SO sowieso) ist "egal"
  • Mich interessiert nun, wieviel der "Laufzeit" (Differenz der beiden Timestamps) in der "gewerteten" Zeit der dazwischen liegenden Tage liegt; die Anteile in den "wertungsfreien" Tageszeiten können einfach entfallen
Wie geht man sowas an, ohne sich in Fallunterscheidungen zu verlieren?! :gruebel: Irgendwie habe ich da grade ein Brett vorm Kopp! :autsch: Ideen? :lupe:

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Di 07.03.17 19:41 
- Nachträglich durch die Entwickler-Ecke gelöscht -

Für diesen Beitrag haben gedankt: Narses
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Di 07.03.17 20:46 
Zitat:
Wie geht man sowas an, ohne sich in Fallunterscheidungen zu verlieren?!


Kommt drauf an ;) Wenn deine Beispiele soweit treffend sind das jeder Tag sich immer gleich verhält, also ein Montag immer wie ein Montag usw. ohne Regeln für besondere Montage dann würde ich so vorgehen.

A. Für jeden ganzen Wochentag die an diesem Tag zu berücksichtigende Zeit bestimmen (7 Konstanten unabhängig von deinem konkreten Zeitraum also was vorberechnetes)
B. Aus deinem konkreten Zeitraum die Randbeißer rausrechnen. Also die beiden Tage am Anfang und Ende die nicht vollständig abgebildet sind und die einzeln behandeln.
C. Anhand der nun kompletten Tage und der Info vom Startdatum(und dessen Wochentag) und der Anzahl folgender Tage kann ich das dann simpel die Anzahl der jeweiligen Wochentage ermitteln und das mit den Konstanten aus A ausmultiplizieren. (17 Montage mal 14400 Sekunden + 18 Dienstage * 14400 Sekunden usw.)
D. Jetzt die beiden anteiligen Tage(die Randbeißer) anhand ihres jeweiligen Wochentags noch dazurechnen. Je nachdem wie komplex die Regeln sind, mehrere zu berücksichtigende gewertete Zeiträume am Tag oder sowas, steckt hier etwas Komplexität. Wäre aber eigentlich der selbe Algo den man auch für A verwenden würde.

Das sollte man so halbwegs sauber in handhabbaren Einzelteilen zerlegen und dann lösen können.

Für diesen Beitrag haben gedankt: Narses
Boldar
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 1555
Erhaltene Danke: 70

Win7 Enterprise 64bit, Win XP SP2
Turbo Delphi
BeitragVerfasst: Mi 08.03.17 05:33 
Oder, wenn es viele/unregelmäßige Regeln gibt, eventuell iterativ mit Regeln aus einem Array:
Jeweils Funktion, die für jeden Zeitpunkt die nächste Intervallgrenze ermittelt plus die Zeit bis dahin zurückgibt. Diese Funktion dann solange ausführen, bisder Zielpinkt erreicht ist, und wenn Man sich in einem Gewertetem intervall befindet, die Zeit zum Ergebnis addieren.
Ich kann das nachher mal versuchen in (Pseudo-)code zu gießen, falls das jetzt zu unverständlich war.

So könnte man jedenfalls auf unregelmäßige Regeln behandeln, die sich nicht Wöchentlich wiederholen.

Für diesen Beitrag haben gedankt: Narses
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Mi 08.03.17 22:47 
Da sich deine Regeln alle auf ganze Tage beziehen, würde ich den Zeitraum erst mal nach Tagen unterteilen. Dann kannst du für jeden Tag eine Überschneidung zwischen dem Zeitintervall und der interessanten Zeit berechnen. Also Top-Down programmiert in etwa so:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
Voraussetzung: Mal orgendlich zusammenfassen
class TimeSpan {
// Start, Ende, whatever
  public GetSeconds()  Oder sowas
public TimeSpan IntersectWith(TimeSpan t)
}

var givenTime = new TimeSpan(ostern, weihnachten)
int sum = 0;
foreach (day in getDays(givenTime))
{
   sum += Evaluate(givenTime, day);
}

function Evaluate(TimeSpan t, Day d)
{
  switch (getWeekDayFor(d))
  {
    case MO: return t.IntersectWith(new TimeSpan(day, 816)).GetSeconds();
    case D1: return t.IntersectWith(new TimeSpan(day, 818)).GetSeconds();
    case MI: return t.IntersectWith(new TimeSpan(day, 816)).GetSeconds();
    case DO: return t.IntersectWith(new TimeSpan(day, 818)).GetSeconds();
    case FR: return t.IntersectWith(new TimeSpan(day, 816)).GetSeconds();
  }  
  return 0;
}

function IEnumerable<TimeSpan> getDays(TimeSpan) liefert nacheinander alle Tage (komplett, als TimeSpan), die sich mit der Zeitspanne überschneiden.


Ich vermute, du wirst um ein switch-case nicht drum herum kommen. (Oder du baust dir eine Zeit-Gewichtungsregel-Auswerte-Software, die du mit beliebigen Wichtungsregeln füttern kannst. Also z.B. Karfreitag wird mit 0 gewertet... aber das muss man wollen)
Das wichtige (was ich sehe) ist, dass dein switch-case nicht unerwartet cases dazu bekommt. Solange die Wochentage die Fälle sind, sehe ich da kein Problem.

Falls es mehrere relevante pro Tag geben kann (Mittagspause?), würde ich es so machen:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
function Evaluate(TimeSpan t, Day d)
{
  const Dictionary<Weekday, TimeSpan[]> rules = new ....;

  rules[Monday] = { new TimeSpan(day, 811.5), new TimeSpan(day, 12.516) };
  rules[Tuesday] = { new TimeSpan(day, 811.5), new TimeSpan(day, 12.518) };
// ...

  var weekday = getWeekDayFor(d);
  if rules.HasKey(weekday )
  {
    return rules[weekday].Select(ts => t.IntersectWith(ts).GetSeconds()).Sum();
  }

  return 0;
}


Aber ich glaube, das ist erst mal Overkill. Erst mal was machen, was funktioniert. Dann wird man sehen, welchen Erweiterungsbedarf man hat.

Für diesen Beitrag haben gedankt: Narses
Narses Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10181
Erhaltene Danke: 1254

W10ent
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Do 09.03.17 00:18 
Moin!

Herzlichen Dank allen Ideen-Spendern. :beer:

Ich habe auch in Boldars Richtung gedacht und deshalb folgenden Ansatz gewählt: (PHP-Pseudocode)
ausblenden PHP-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
function getWorkTime(DateTime $start, DateTime $stop) {
  $sow = clone $start;
  assertNextWorkDay($sow); // sicherstellen, dass das übergebene DateTime-Objekt auf einen Werktag zeigt; wenn nicht, tageweise in die Zukunft gehen
  setStartOfWork($sow); // Uhrzeit im übergebenen DateTime-Objekt auf den Arbeitsbeginn des Tages setzen
  if ($sow < $start// liegt der Arbeitsbeginn vor dem Start-Zeitpunkt
    $sow = clone $start// dann den Startzeitpunkt nehmen
  $eow = clone $stop;
  assertPrevWorkDay($eow); // sicherstellen, dass das übergebene DateTime-Objekt auf einen Werktag zeigt; wenn nicht, tageweise in die Vergangenheit gehen
  setEndOfWork($eow); // Uhrzeit im übergebenen DateTime-Objekt auf das Arbeitsende des Tages setzen
  if ($eow > $stop// liegt das Arbeitsende hinter dem Start-Zeitpunkt
    $eow = clone $stop// dann den Endezeitpunkt nehmen
  $delta = 0// ermittelte Differenz zwischen $start und $stop
  while ($sow < $eow) {
    if (isSameDay($sow$eow)) {
      $delta += secondsBetween($sow$eow);
    }
    else {
      $this_eow = clone $sow;
      setEndOfWork($this_eow);
      if ($sow < $this_eow)
        $delta += secondsBetween($sow$this_eow);
    }
    nextWorkDay($sow); // das übergebene DateTime-Objekt auf den nächsten Werktag stellen, nicht-Arbeitstage überspringen
    setStartOfWork($sow);
  }
  return $delta;
Dieser Ansatz funktioniert unter der Annahme, dass es pro Werktag immer nur eine "gewertete" Zeitspanne gibt. :idea: (das reicht mir allerdings schon).

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.