at immer am ersten (z.B. Dienstag) oder dritten (z.B. Donnerstag) ausführen lass

Begonnen von F_Klee, 15 Februar 2024, 16:04:13

Vorheriges Thema - Nächstes Thema

F_Klee

Hallo Leute,
um ein at immer am Letzten eines Monats ausführen zu lassen gibt es ja die Funktion at_ultimo(). Schön wäre vielleicht eine weitere Funktion, damit ein at immer am (z.B) ersten Dienstag im Monat ausgeführt wird. Häufig werden ja Treffen immer am ersten und dritten Dienstag eines Monats zu einer festen Uhrzeit durchgeführt (kann natürlich auch jeder andere Wochentag sein  ;) ). Man müsste also den Wochentag (1-7), 1-4 für ersten bis vierten Tag und die Uhrzeit übergeben. Eine Formel für Excel sieht so aus:

=A2-1-WOCHENTAG(A2-WT;3)+7*X

wobei A2 für das Datum des ersten Tags des aktuellen Monats, WT für den Wochentag (Montag=1) und X für den ersten bis vierten Tag steht.

Ich hatte mir Funktion at_ultimo() angesehen, habe aber mehr Fragen als Antworten  :) . Sicher kann jemand mit mehr Ahnung das recht schnell umsetzen. Könnte ja für mehrere Anwender interessant sein.

Gruß
Frank

JoWiemann

Jörg Wiemann

Slave: RPi B+ mit 512 MB, COC (868 MHz), CUL V3 (433.92MHz SlowRF); FHEMduino, Aktuelles FHEM

Master: CubieTruck; Debian; Aktuelles FHEM

rudolfkoenig

Eine aehnliche Funktionalitaet ist im holiday Modul implementiert: https://fhem.de/commandref_modular.html#holiday

Kurz: man definiert eine .holiday Datei in /opt/fhem/FHEM (Beispiele siehe /opt/fhem/FHEM/holiday/*), man legt eine holiday Inastanz an mit passenden Namen (nicht ganz intuitiv, ist aber so), und wertet die yesterday/today/tomorrow Readings dieser Instanz in einem at aus.

F_Klee

Hallo Jörg,
ich wäre nie auf die Idee gekommen, nach "Alarmclock" zu suchen  :) . Werde ich mal probieren. Danke!

betateilchen

Man könnte das wahrscheinlich auch über die cron-library machen, die es inzwischen in FHEM gibt.

Müsste man vermutlich in die 99_myUtils.pm bauen.

Oder irgendjemand erbarmt sich irgendwann, das at-Modul so zu erweitern, dass es auch Zeitangaben im Format eines crontab-Eintrags verarbeitet.
(oder baut - noch besser - gleich ein neues Modul 91_cron.pm als Alternative zu at)

Für den "ersten" bestimmten Wochentag eines Monats würde man das so bauen:

10 3 1-7 * *
und müsste dann im Ausführungsteil einfach nur noch prüfen, ob der gewünschte $wday für z.B. Mittwoch erreicht ist.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

herrmannj

Zitat von: betateilchen am 16 Februar 2024, 15:08:21Man könnte das wahrscheinlich auch über die cron-library machen, die es inzwischen in FHEM gibt.

Die cronlib kann das sogar eleganter weil sie einen erweiterten syntax implementiert. Die lib wird aktuell von JsonMod und (soweit ich weiß) einem Modul von JoWiemann verwendet. Ein 90_cron-pm habe ich geplant, falls jemand dort einspringen will: sehr gerne und ich unterstütze.

Anlaufstelle zum Einlesen des erweiterten syntax sind die Tests: https://svn.fhem.de/fhem/trunk/fhem/t/FHEM/90_Cron/10_Cronlib.t

Der erste Dienstag eines Monats kann so:
0 0 * * 2#F
0 0 * * 2#1
0 0 * * Tue#F
0 0 * * Tue#1
Letzter:
0 0 * * 2#L
0 0 * * 2#-1
oder
0 0 * * Tue#L
0 0 * * Tue#-1
Vorletzter:
0 0 * * 2#-2
btw: Freitag der 13:
0 0 13 * &5
usw...

F_Klee

Hallo Leute,
Alarmclock hat leider nicht (immer) funktioniert. Deshalb habe ich mich doch daran gesetzt, diese Funktion umzusetzen. Hier mein Ergebnis:
########################################
#
# Berechnung des Datums des
# x.ten Wochentags im Monat
#
# x=1-4 x.ter Wochentag im Monat
# x=9   letzter Wochentag im Monat
# w=1-7 Wochentag, 1=Montag
# h,m,s Uhrzeit Stunde, Minute, Sekunde
#
sub at_xwday($$$$$) {
    my ($x,$w,$h,$m,$s) = @_;

    my $ziel_tag = 0;

    # akt. Datum und Uhrzeit in Variablen aufteilen
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
        localtime(time);

    # bei Neuberechnung einen Monat addieren
    my $add = $data{AT_RECOMPUTE} ? 1 : 0;
    $mon = $mon + $add;

    my ($nm, $ny) = ($mon == 12) ? (0,$year+1) : ($mon,$year);

    # Letztes Vorkommen des Wochentags berechnen?
    if ($x == 9) {
        # Berechnen des letzten Tags des Monats
        # ersten Tag des Folgemonats als timestamp
        my ($xm, $xy) = ($mon == 11) ? (0,$year+1) : ($mon+1,$year);
        my $nt = mktime(0, 0, 0, 1, $xm, $xy);

        # letzter Tag des aktuellen Monats = erster Tag des Folgemonats minus ein Tag
        my ($letzter_tag_des_monats, $letzter_wochentag) = (localtime($nt - DAYSECONDS))[3,6];

        # Prüfen, ob der letzte Wochentag im Monat mit dem gewünschten Wochentag übereinstimmt
        $ziel_tag = $letzter_tag_des_monats;
        if ($letzter_wochentag > $w) {
           # Falls der letzte Wochentag später im Monat ist, subtrahiere die Differenz
           my $differenz = $letzter_wochentag - $w;
           $ziel_tag -= $differenz;
        } elsif ($letzter_wochentag < $w) {
           # Falls der letzte Wochentag früher im Monat ist, subtrahiere 7 plus die Differenz
           my $differenz = $w - $letzter_wochentag;
           $ziel_tag -= (7 - $differenz);
        }
    } else {
        # Berechne den Tag des ersten Vorkommens des Wochentags im Monat
        my $erster_tag = 1;
        my $erster_tag_des_monats = (localtime(mktime(0,0,0,$erster_tag,$nm,$ny)))[6];
        my $erstes_vorkommen = ($w - $erster_tag_des_monats + 7) % 7 + 1;

        # Berechne den Tag des x.ten Vorkommens des Wochentags im Monat
        $ziel_tag = $erstes_vorkommen + 7 * ($x - 1);

        # Überprüfe, ob das Datum im gültigen Bereich liegt
        if ($ziel_tag > 31 || $ziel_tag < 1) {
          return "Ungültiges Datum";
        }
    }
    return mktime($s,$m,$h,$ziel_tag,$nm,$ny);
}

Die ersten Tests liefen erfolgreich. "Letzter Wochentag" stimmt in diesem Monat. Am 28. kann ich berichten, ob die Neuberechnung ebenfalls funktioniert. ;D  Ihr könnt ja mal drüber schauen, ob es noch etwas zu optimieren gibt und die Funktion bei Bedarf gerne in das at-Modul einfügen.

F_Klee

"Letzter Wochentag im Monat" wurde für April auch korrekt berechnet. Sollte also funktionieren  :)