Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Kompliziertes Problem mit Wochentagberechnung (https://www.delphipraxis.net/193679-kompliziertes-problem-mit-wochentagberechnung.html)

Glados 28. Aug 2017 18:36


Kompliziertes Problem mit Wochentagberechnung
 
Ich sitze gerade vor einem Problem und ich weiß nicht so recht weiter.

In meinem Programm kann ich einen Task für ausgewählte Wochentage deaktivieren.
Wird der Task am Dienstag gestartet und Dienstag ist in der Liste der ausgewählten Wochentage, dann gehts nicht weiter - alles gewollt bisher.

An einer anderen Stelle im Code gibt es eine kleine Berechnung.
Sagen wir mal es ist der 31.08.2017 gegeben. Der 31.08 ist ein Donnerstag und befindet sich in der Liste.

Das Problem:
wie erhalte ich den nächst-möglichen freien Tag, der nicht in der Liste steht?

Mein aktueller Code sieht so aus. Moben gegebenem Szenario (Datum 31.08.2017) funktioniert er nicht, da die while-Schleife natürlich direkt abbricht:
Delphi-Quellcode:
// aExcludeDays =
// 0, -1, -1, -1, -1, -1, -1 // 0 = False, -1 = True (True = verbotener Wochentag)

// StartTimestamp =
// 31.08.2017 als Unix-Timestamp

// DayOfWeek =
// Der aktuelle Wochentag

// Wandelt das Format in ISO 8601 (Montag erster Tag) um
function GetDayOfWeek(const DateTime: TDateTime): ShortInt;
begin
 Result := -1;
 case DayOfWeek(DateTime) of
  1: // Sunday
   Result := 6;
  2: // Monday
   Result := 0;
  3: // Tuesday
   Result := 1;
  4: // Wednesday
   Result := 2;
  5: // Thursday
   Result := 3;
  6: // Friday
   Result := 4;
  7: // Saturday
   Result := 5;
 end;
end;

function GetNextAvailableWeekDay(const aExcludeDays: TArray<string>; StartTimestamp, DayOfWeek: Integer): Integer;
var
 iTmp: Integer;
begin
 if DayOfWeek >= 0 then
  begin
   iTmp := 0;

   while (Length(aExcludeDays) > DayOfWeek) and (StrToBool(aExcludeDays[DayOfWeek])) do
    begin
     if iTmp = 7 then
      begin
       StartTimestamp := 0;
       Break;
      end;

     Inc(StartTimestamp, 86400);
     DayOfWeek := TDateUtils.GetDayOfWeek(IncSecond(Now, StartTimestamp));
     Inc(iTmp);
    end;
  end;

 Result := StartTimestamp;
end;
Ich hoffe mich versteht jemand?


Edit
Ich glaube ich habs. Bin mir aber nicht sicher. GetDayOfWeek() geht sicher auch schöner
Delphi-Quellcode:
   while (Length(aExcludeDays) > aDayOfWeek) do // and (StrToBool(aExcludeDays[DayOfWeek])) do
    begin
     if iTmp = 8 then // 8 statt 7 // Wenn die Woche 1x komplett durch ist, Endlosschleife vermeiden!
      begin
       StartTimestamp := 0;
       Break;
      end;

     Inc(StartTimestamp, 86400);
     aDayOfWeek := TDateUtils.GetDayOfWeek(StartTimestamp); // hier "StartTimestamp" statt "IncSecond(Now, StartTimestamp)"
     Inc(iTmp);

     if not StrToBool(aExcludeDays[aDayOfWeek - 1]) then // Schlussprüfung
      Break;
    end
Ok ich habs doch nicht.

himitsu 28. Aug 2017 20:42

AW: Kompliziertes Problem mit Wochentagberechnung
 
GetDayOfWeek .... Delphi-Referenz durchsuchenDayOfWeek oder Delphi-Referenz durchsuchenDayOfTheWeek :zwinker:

Und wenn die While-Schleife zu früh abbricht, dann stimmt deine Bedingung wohl nicht.
Oder wenn die Schleife immer mindestens einmal durchlaufen soll, dann es womöglich mal mit einer Repeat-Schleife versuchen.

Glados 28. Aug 2017 21:28

AW: Kompliziertes Problem mit Wochentagberechnung
 
Also diese Antwort hat mir jetzt leider nicht geholfen :roll:
Zitat:

GetDayOfWeek .... Delphi-Referenz durchsuchenDayOfWeek oder Delphi-Referenz durchsuchenDayOfTheWeek
Jetzt hab ichs kapiert. Aber oben schrieb ich:
Zitat:

// Wandelt das Format in ISO 8601 (Montag erster Tag) um
Deswegen habe ich ja meine eigene Funktion - damit bei mir die Woche mit Montag beginnt.

Zitat:

Und wenn die While-Schleife zu früh abbricht, dann stimmt deine Bedingung wohl nicht.
Oder wenn die Schleife immer mindestens einmal durchlaufen soll, dann es womöglich mal mit einer Repeat-Schleife versuchen.
Passt nicht in mein Szenario.

Ich habs jetzt gelöst. Schön ist aber anders
Delphi-Quellcode:
// aExcludeDays =
// 0, -1, -1, -1, -1, -1, -1 // 0 = False, -1 = True (True = verbotener Wochentag)

// StartTimestamp =
// 31.08.2017 als Unix-Timestamp

// Beim Aufruf von dieser Funktion mit übergebenem Start-Datum 31.08.2017 (Donnerstag) und obigem Array,
// muss das Resultat der 04.09.2017 (Montag) sein, da er kein "verbotener" Tag ist.

procedure GetNextAvailableWeekDay(const aExcludeDays: TArray<string>; StartTimestamp: Integer): Integer;
var
 iTmp, aDayOfWeek: Integer;
 bDayExcluded: Boolean;
begin
 iTmp := 0;

 // Prüfe, ob das Startdatum (hier 31.08.2017) ein "verbotener" Tag ist.
 aDayOfWeek := TDateUtils.GetDayOfWeek(UnixToDateTime(StartTimestamp));
 bDayExcluded := StrToBool(aExcludeDays[aDayOfWeek]);

 // Wenn der Tag "verboten" wurde (mit -1 oben im Array), dann springe hier rein
 if bDayExcluded then
  begin
   while True do
    begin
     Inc(StartTimestamp, 86400); // Füge dem Startdatum einen Tag hinzu
     aDayOfWeek := TDateUtils.GetDayOfWeek(UnixToDateTime(StartTimestamp)); // Hole den Wochentag (Woche beginnt mit Montag)

     // Prüfe erneut, ob das Datum ein "verbotener" Tag ist
     bDayExcluded := StrToBool(aExcludeDays[aDayOfWeek]);

     // Ist das Datum ein erlaubter/freier Tag, dann können wir hier abbrechen und es als Ergebnis verwenden
     if not bDayExcluded then
      Break;

     Inc(iTmp);
     if iTmp = 8 then
      begin
       StartTimestamp := 0;
       Break;
      end;
    end;
  end;

 Result := StartTimestamp;
end;

Uwe Raabe 28. Aug 2017 23:41

AW: Kompliziertes Problem mit Wochentagberechnung
 
Zitat:

Zitat von Glados (Beitrag 1379774)
Schön ist aber anders

Wie wär's denn damit?
Delphi-Quellcode:
type
  TDayOfWeekSet = set of 0..6;

// Wandelt das Format in ISO 8601 (Montag erster Tag) um
function GetDayOfWeek(const DateTime: TDateTime): ShortInt;
begin
  Result := (DayOfWeek(DateTime) + 5) mod 7;
end;

function GetNextAvailableWeekDay(const aExcludeDays: TDayOfWeekSet; StartTimestamp: Integer): Integer;
var
  dt: TDateTime;
  I: Integer;
begin
  dt := UnixToDateTime(StartTimestamp);
  for I := 0 to 6 do begin
    if not (GetDayOfWeek(dt) in aExcludeDays) then
      Exit(DateTimeToUnix(dt));
    dt := IncDay(dt);
  end;
  Result := 0;
end;

procedure TestCase;
var
  dt: TDateTime;
begin
  dt := UnixToDateTime(GetNextAvailableWeekDay([1..6], DateTimeToUnix(EncodeDate(2017, 08, 31))));
  Assert(SameDate(dt, EncodeDate(2017, 09, 04)));
end;
Ist nur so ein Gefühl, aber die negative Logik bei aExcludeDays würde ich in aAllowedDays umkehren. Aber das hängt vielleicht auch vom Kontext ab.

himitsu 29. Aug 2017 00:37

AW: Kompliziertes Problem mit Wochentagberechnung
 
Du willst den "nächsten" Tag, also exclusive dem Aktuellen/Übergebenen.
Da muß die Schleife mindestens einmal durchlaufen werden, also prüft man am Ende (repeat-until)
soll inkl. des Aktuellen/Übergebenen auswertet werden, dann vor der Schleife prüfen (while-do).

Und scheinbar hast du die Hilfe nicht gesehn?
GetDayOfWeek = DayOfTheWeak
Zitat:

DayOfTheWeek is ISO 8601 compliant
Warum ist aExcludeDays ein TArray<string> anstatt einem TArray<Boolean>, wobei da ein SET-OF-WeekDays eventuell verständlicher ist.
siehe TDayOfWeekSet ... aber eventuell auch als 1..7, je nach Datenformat
PS: Für die "unverständlichen" Zahlen gibt es auch Wochentagskonstanten, die man verwenden darf.

Und wieso muß DayOfWeek an die Funktion übergeben werden, wenn du auch den Startwert aus StartTimestamp rausholen kannst?

PS: Eine Fehlerprüfung (z.B. Assert) am Anfang wäre nicht schlecht, falls jemand auf die Idee kommt und alle Tage verbietet. -> Endlosschleife

TigerLilly 29. Aug 2017 07:31

AW: Kompliziertes Problem mit Wochentagberechnung
 
>while True do

So was ist immer ein repeat/until.

Glados 29. Aug 2017 12:39

AW: Kompliziertes Problem mit Wochentagberechnung
 
Zitat:

Warum ist aExcludeDays ein TArray<string> anstatt einem TArray<Boolean>, wobei da ein SET-OF-WeekDays eventuell verständlicher ist.
Weil der Datendsatz 0, -1, -1, -1, -1, -1, -1 aus einer Textdatei kommt.
Diesen lese ich mit Ini ReadString, das Resultat packe ich in ein Array und übergebe es der Funktion.

In ein Set würde ich meine Auswahl folgendermaßen speichern
Delphi-Quellcode:
for i := 0 to 6 do
 begin
  aCheckBox := (FindComponent('cbDay' + IntToStr(i)) as TCheckBox);

  if Assigned(aCheckBox) and aCheckBox.Checked then
   aDayOfWeekSet := aDayOfWeekSet + [aCheckBox.Tag];
 end;
Nur wie soll man so etwas in eine Datei abspeichern?

Uwe Raabe 29. Aug 2017 13:08

AW: Kompliziertes Problem mit Wochentagberechnung
 
Zitat:

Zitat von Glados (Beitrag 1379818)
Nur wie soll man so etwas in eine Datei abspeichern?

Da in diesem Set gerade mal 7 Bits belegt sind, würde ein Cast auf bzw. von Byte reichen.

Glados 29. Aug 2017 13:31

AW: Kompliziertes Problem mit Wochentagberechnung
 
Ok speichern so
Delphi-Quellcode:
IniS.WriteInteger(section, ident, Byte(aDayOfWeekSet));
Das funktioniert soweit.

Mit dem Laden tue ich mich aber noch schwer
Delphi-Quellcode:
var
 exclude: TDayOfWeekSet;
begin
 exclude := TDayOfWeekSet(Byte(IniS.ReadInteger(section, ident, 0))); // in der Ini steht 8/Mittwoch

 // cdDay2 = Mittwoch
 cbDay2.Checked := 8 in exclude;
end;

Uwe Raabe 29. Aug 2017 13:47

AW: Kompliziertes Problem mit Wochentagberechnung
 
Zitat:

Zitat von Glados (Beitrag 1379829)
Ok speichern so
Delphi-Quellcode:
IniS.WriteInteger(section, ident, Byte(aDayOfWeekSet));
Das funktioniert soweit.

Mit dem Laden tue ich mich aber noch schwer

Dann vielleicht so:
Delphi-Quellcode:
  Byte(exclude) := IniS.ReadInteger(section, ident, 0);

Glados 29. Aug 2017 13:52

AW: Kompliziertes Problem mit Wochentagberechnung
 
Ok das wusste ich nicht. Ich dachte das Casten beim Auslesen + das "in" würden reichen.
Mh das funktioniert auch nicht, obwohl Byte(exclude) nun "8" entspricht.

Uwe Raabe 29. Aug 2017 13:52

AW: Kompliziertes Problem mit Wochentagberechnung
 
Oder ganz komfortabel mit einem Record Helper:
Delphi-Quellcode:
  TDayOfWeekSetHelper = record helper for TDayOfWeekSet
    function GetAsInteger: Integer;
    procedure SetAsInteger(const Value: Integer);
    property AsInteger: Integer read GetAsInteger write SetAsInteger;
  end;

function TDayOfWeekSetHelper.GetAsInteger: Integer;
begin
  Result := Byte(Self);
end;

procedure TDayOfWeekSetHelper.SetAsInteger(const Value: Integer);
begin
  Byte(Self) := Value;
end;

...
  IniS.WriteInteger(section, ident, aDayOfWeekSet.AsInteger);
...
  exclude.AsInteger := IniS.ReadInteger(section, ident, 0);

Glados 29. Aug 2017 13:57

AW: Kompliziertes Problem mit Wochentagberechnung
 
Werde ich definitiv noch umsetzen.
Aber wieso schlägt denn ein Vergleich
Delphi-Quellcode:
cbDay2.Checked := 8 in exclude;
fehl? Die 8 ist ganz sicher da drin vorhanden.

Wie ich aktuell prüfe
Delphi-Quellcode:
cbDay2.Checked := 8 in exclude;
cbDay1.Checked := 4 in exclude;
cbDay0.Checked := 2 in exclude;

Uwe Raabe 29. Aug 2017 14:17

AW: Kompliziertes Problem mit Wochentagberechnung
 
Zitat:

Zitat von Glados (Beitrag 1379838)
Werde ich definitiv noch umsetzen.
Aber wieso schlägt denn ein Vergleich
Delphi-Quellcode:
cbDay2.Checked := 8 in exclude;
fehl? Die 8 ist ganz sicher da drin vorhanden.

Wie ich aktuell prüfe
Delphi-Quellcode:
cbDay2.Checked := 8 in exclude;
cbDay1.Checked := 4 in exclude;
cbDay0.Checked := 2 in exclude;


So funktionieren Sets aber nicht. Die 8 kann gar nicht im Set vorkommen, da die Wochentage nur von 0..6 definiert sind.

Delphi-Quellcode:
cbDay2.Checked := 2 in exclude; // Mittwoch
cbDay1.Checked := 1 in exclude; // Dienstag
cbDay0.Checked := 0 in exclude; // Montag
Oder besser gleich so deklarieren:

Delphi-Quellcode:
type
  TDayOfWeek = (dowMontag, dowDienstag, dowMittwoch, dowDonnerstag, dowFreitag, dowSamstag, dowSonntag);
  TDayOfWeekSet = set of TDayOfWeek;

Glados 29. Aug 2017 14:23

AW: Kompliziertes Problem mit Wochentagberechnung
 
Ok hier noch einmal alles was ich bisher habe

Speichern
Delphi-Quellcode:
var
 aDayOfWeekSet: TDayOfWeekSet ;
begin
for i := 0 to 6 do
 begin
  aCheckBox := (FindComponent('cbDay' + IntToStr(i)) as TCheckBox);

  if Assigned(aCheckBox) and aCheckBox.Checked then
   aDayOfWeekSet := aDayOfWeekSet + [aCheckBox.Tag]; // Das Tag jeder CheckBox enthält Zahlen von 0-6 (Montag-Sonntag)
 end;

 IniS.WriteInteger(section, ident, Byte(aDayOfWeekSet));
end;
Lesen
Delphi-Quellcode:
 Byte(exclude):= IniS.ReadInteger(section, ident, 0);

 ShowMessage(IntToStr(Byte(exclude))); // z.B.: 28 = Dienstag, Mittwoch, Donnerstag waren "checked"

 cbDay2.Checked := 2 in exclude;
 cbDay1.Checked := 1 in exclude;
 cbDay0.Checked := 0 in exclude;
Speichern funktioniert, lesen leider nicht :(

generic 29. Aug 2017 14:30

AW: Kompliziertes Problem mit Wochentagberechnung
 
Liste der Anhänge anzeigen (Anzahl: 1)
Kennst du Cromis cron-scheduler? Du erfindest das Rad gerade erneut.


Ich hatte da 2014 mal drüber geschrieben:
https://entwickler.de/online/develop...al-114967.html

Die Cromis homepage geht gerade bei mir nicht:
http://web.archive.org/web/201603150...ron-scheduler/

Uwe Raabe 29. Aug 2017 14:31

AW: Kompliziertes Problem mit Wochentagberechnung
 
Zitat:

Zitat von Glados (Beitrag 1379846)
Delphi-Quellcode:
 ShowMessage(IntToStr(Byte(exclude))); // z.B.: 28 = Dienstag, Mittwoch, Donnerstag waren "checked"

Das stimmt schon mal nicht! 28 = 4 + 8 + 16 entspricht Mittwoch, Donnerstag, Freitag. Schau mal nach, was in deinen Checkbox.Tags wirklich steht.

Übrigens:
Delphi-Quellcode:
  aDayOfWeekSet := aDayOfWeekSet + [aCheckBox.Tag];

geht auch so:
Delphi-Quellcode:
  Include(aDayOfWeekSet, aCheckBox.Tag);

Glados 29. Aug 2017 14:38

AW: Kompliziertes Problem mit Wochentagberechnung
 
Zitat:

Schau mal nach, was in deinen Checkbox.Tags wirklich steht.
Du hattest recht. Ich hatte eine 1-basierte Zählung in den Tags, nicht 0-basiert :pale:

Zitat:

Kennst du Cromis cron-scheduler? Du erfindest das Rad gerade erneut.
Vor Cron habe ich mich schon mein ganzes Leben lang erfolgreich versteckt was wohl daran liegt, dass ich es nicht leiden kann genau so wenig wie Linux.

Klar, das ist extrem flexibel, keine Frage! Aber das jetzt alles umbauen... nee.

Es funktioniert nun alles
Danke an alle die geholfen haben dieses/mein Mysterium zu lösen.

himitsu 29. Aug 2017 16:27

AW: Kompliziertes Problem mit Wochentagberechnung
 
Wenn man das als SET mit "Namen" deklariert hat, könnte man auch über die TypeInfo oder RTTI das SET von/in einen String umwandeln.

Glados 29. Aug 2017 16:29

AW: Kompliziertes Problem mit Wochentagberechnung
 
Du meinst damit die Controls dahinter die richtigen Captions bekommen?

himitsu 29. Aug 2017 21:28

AW: Kompliziertes Problem mit Wochentagberechnung
 
Damit in der INI etwas Verständliches drin steht. :zwinker:


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:42 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz