Michael Schröpl: Uhrzeitberechnung

Hallo Leute,

ich bin gerade dabei, ein bißchen in Perl mit Uhrzeiten herumzurechnen.

Was ich haben möchte, das ist eine saubere Erkennung eines Datumswechsels auf meiner Maschine (weil sich mit diesem Datumswechsel die Aufgabenstellung meines laufenden Programms ändert).

Das sieht ja auf den ersten Blick nicht so arg schwer aus:

my $now        = time();
    my $end_of_day = $now - ($now % 86400) + 86400;

und dann

while (time() <= $end_of_day)
          { ... }

Tja, wenn ich nun in Greenwich wohnen würde und es keine Sommerzeit gäbe, dann wäre ich bereits fertig.
Beides ist aber nicht der Fall (vor allem im Moment).
Mein Wert liegt genau zwei Stunden daneben (die 'Umschaltung' erfolgt derzeit also um 2 Uhr früh).

Eine der beiden Stunden kann ich "per Definition" abziehen, weil ich die Zeitzone, in welcher das Programm laufen wird, mit ziemlicher Sicherheit kenne (nämlich "hier").
Ich fände es aber schöner, wenn ich irgendwo die Anzahl der Zeitzonen ablesen könnte. Mein erster Blick fiel auf die Environment-Variable "TZ" - aber da steht "MEZ" drin, nicht etwa "+1" oder etwas ähnlich Numerisches, was sich als Faktor für 3600 eignen würde, so daß ich das Produkt der beiden von meinem time()-Wert abziehen könnte. Eine Tabelle aller symbolischen TZ-Werte in mein Programm einzubauen fände ich nicht sonderlich ästhetisch - zumal die womöglich noch nicht mal universell eindeutig sein müssen, denke ich mal ...

Die andere Stunde Differenz ist dann die Sommerzeit.
Über localtime() bekomme ich im letzten der vielen Parameter "isdst" ein Flag, das mir (laut Google - meine Perl5-Doku setzt an dieser Stelle einfach Kenntnisse des C-Compilers voraus, pfui Teufel) sagt, ob wir Sommerzeit haben oder nicht oder ob das System darüber keine Aussage machen kann ... (letzteres fände ich nicht so toll).
Auf meiner Maschine bekomme ich in der Tat den erfreulichen Wert 1 zurück. Und im Moment ist es ja wohl offensichtlich so, daß "Sommerzeit" identisch ist mit "genau eine Stunde Verschiebung" ... solches implizite Wissen möchte ich allerdings nicht so wahnsinnig gerne in meinen Quelltext einbrennen, falls es auch irgendwie eine andere Lösung gäbe, wo man die tatsächliche Zeit-Differenz ablesen könnte.

Was ich auch tun könnte, das wäre, mit localtime() und gmtime() die beiden entsprechenden Uhrzeiten als Komponenten-Felder zu berechnen und dann irgendwie daraus die Differenz zu berechnen.
Das würde zwar sämtliche Effekte subsummieren ... aber ich müßte beides erst mal wieder in Sekunden zurückrechnen und dann die Differenz aus beiden Werten bilden ... und wie mache ich das im allgemeinen Fall, ohne komplette Kalender- und Schaltjahresrechnerei?
Ich könnte mit der Differenz der beiden Stunden-Werte arbeiten und den Rest vernachlässigen ... dann aber würde ich wieder implizite Annahmen über meinen Standort verwenden müssen ... oder?

Daher meine Frage:

  • Gibt es in Perl irgendwelche (von mir übersehenen) Variablen,
      Funktionen oder was auch immer, welche mir (zuverlässiger als oben
      beschrieben und ohne viel Rechnerei)
       a) die Zeitzonenentfernung von Greenwich bzw.
       b) die Zeitverschiebung durch Sommerzeit
      jeweils als Zahlenwerte zurückliefern würden?
    Letzten Endes will ich $end_of_day korrekt gesetzt bekommen.

Lösungen _ohne_ erforderliche Installation irgendwelcher CPAN-Module
würde ich bevorzugen (das Zeug müßte ggf. auf einen Haufen Produktionsmaschinen drauf - das würde ich gerne vermeiden, zumal ich das normalen CPAN-online-Installationsverfahren wg. Firewall nicht so ohne weiteres verwenden darf).

Viele Grüße
      Michael

  1. Hi,

    • Gibt es in Perl irgendwelche (von mir übersehenen) Variablen,
        Funktionen oder was auch immer, welche mir (zuverlässiger als oben
        beschrieben und ohne viel Rechnerei)

    was hälst Du davon, mittels Date::Calc (falls nötig) einen Tag hinzuzuzählen, und Time::Local zu verwenden, um "heute plus 1, 0:00 Uhr" zu ermitteln?

    Lösungen _ohne_ erforderliche Installation irgendwelcher CPAN-Module

    Hm, auch ohne Standard-Module? :-)

    (das Zeug müßte ggf. auf einen Haufen Produktionsmaschinen drauf - das würde ich gerne vermeiden, zumal ich das normalen CPAN-online-Installationsverfahren wg. Firewall nicht so ohne weiteres verwenden darf).

    Bei hinreichend gleichartigen Systemen reicht vermutlich scp oder ähnliches.

    Cheatah

    1. Hallo Cheatah,

      Lösungen _ohne_ erforderliche Installation irgendwelcher CPAN-Module
      Hm, auch ohne Standard-Module? :-)

      ich habe auf der Entwicklungsmaschine Perl 5.005_03 drauf, das installiere ich aber nicht selbst.
      Was genau auf den anderen Maschinen drauf ist (womöglich weniger), weiß ich im Moment noch nicht so genau - und auch nicht, wie viele Maschinen das letztlich werden ... es ist nicht mal ausgeschlossen, daß mein Daemon in ein paar Monaten eine eigene Maschine bekommt und mit den anderen Maschinen kommunizieren wird, das ist alles noch ein wenig arg im Fluß ...

      (das Zeug müßte ggf. auf einen Haufen Produktionsmaschinen drauf -
      das würde ich gerne vermeiden, zumal ich das normalen CPAN-online-
      Installationsverfahren wg. Firewall nicht so ohne weiteres verwenden
      darf).
      Bei hinreichend gleichartigen Systemen reicht vermutlich scp oder
      ähnliches.

      Ups - das sagt mir jetzt erst mal nichts.
      Was die Maschinen angeht: Ja, die sollten ziemlich gleichartig sein.

      Viele Grüße
            Michael

      1. Hi,

        ich habe auf der Entwicklungsmaschine Perl 5.005_03 drauf,

        hm, eigentlich sollte das reichen. Allerdings solltest Du Dich auch nicht scheuen, Deinem Arbeitgeber gewisse Minimalanforderungen vorzuschreiben - beispielsweise Perl ;-) aber eben auch die wenigen Module. AFAIK ist Time::Date reiner Perl-Code, braucht also nicht mal kompiliert zu werden; das kann _jeder_ in Minutenschnelle installieren.

        Was genau auf den anderen Maschinen drauf ist (womöglich weniger), weiß ich im Moment noch nicht so genau - und auch nicht, wie viele Maschinen das letztlich werden ... es ist nicht mal ausgeschlossen, daß mein Daemon in ein paar Monaten eine eigene Maschine bekommt und mit den anderen Maschinen kommunizieren wird, das ist alles noch ein wenig arg im Fluß ...

        Argh, ich _liebe_ so exakte Anforderungen :-)

        Bei hinreichend gleichartigen Systemen reicht vermutlich scp oder
        ähnliches.

        Ups - das sagt mir jetzt erst mal nichts.

        scp ist "secure copy", mit dem Du unter Unix zwischen verschiedenen Maschinen kopieren kannst. Wenn Dir FTP, CVS o.ä. lieber ist, kannst Du das natürlich auch nehmen :-)

        Was die Maschinen angeht: Ja, die sollten ziemlich gleichartig sein.

        Das "gleichartig" bezog sich vor allem darauf, dass manche Module kompiliert sein müssen. Somit hättest Du also große Vorteile, wenn Du später ein "komplexeres" Modul brauchst.

        Cheatah

        1. Hallo Cheatah,

          Allerdings solltest Du Dich auch nicht scheuen, Deinem
          Arbeitgeber gewisse Minimalanforderungen vorzuschreiben -
          beispielsweise Perl ;-) aber eben auch die wenigen Module.

          das Problem ist, daß auf der Maschine bereits ein Produkt läuft, das eine definierte Installations- und Betriebsvoraussetzung hat, und ich zu diesem Produkt nur add-ons baue. Das Produkt ist von einer Partnerfirma - denen kann ich nicht vorschreiben, was ich alles gerne auf der Maschine hätte.

          Wenn ich aber irgendwelches Zeug am Perl-Interpreter ändere (den die auch voraussetzen - deshalb ist er ja bereits verfügbar) und dann läuft irgendwas von _deren_ Software nicht mehr, dann bin natürlich _ich_ schuld ... (bis zum Beweis des Gegenteils).

          Deshalb möchte ich an dieser Stelle lieber gar nicht anfangen, etwas drehen zu müssen. Je autarker meine add-ons sind, desto weniger besteht die Gefahr, daß ich versehentlich irgendwas kaputt mache.

          Was genau auf den anderen Maschinen drauf ist (womöglich
          weniger), weiß ich im Moment noch nicht so genau - und
          auch nicht, wie viele Maschinen das letztlich werden ...
          es ist nicht mal ausgeschlossen, daß mein Daemon in ein
          paar Monaten eine eigene Maschine bekommt und mit den
          anderen Maschinen kommunizieren wird, das ist alles noch
          ein wenig arg im Fluß ...
          Argh, ich _liebe_ so exakte Anforderungen :-)

          Die Anforderung lautet im Wesentlichen: "Die bisherige Lösung ist untauglich, und der Source-Code ist nicht verfügbar - also wegwerfen und neu schreiben." Und das mache ich gerade. ;-)

          Daß es letztlich viele gleiche Maschinen werden und daß die Idee besteht, bestimmte Services von dedizierten anderen Maschinen zu importieren, ist eine vergleichsweise neue Architektur-Randbedingung (mit der ich aber prima leben kann, in einem Universum von HTTP und maschinenübergreifenden mySQL-Zugriffen).

          Viele Grüße
                Michael

          1. Hi,

            das Problem ist, daß auf der Maschine bereits ein Produkt läuft, das eine definierte Installations- und Betriebsvoraussetzung hat, und ich zu diesem Produkt nur add-ons baue. Das Produkt ist von einer Partnerfirma - denen kann ich nicht vorschreiben, was ich alles gerne auf der Maschine hätte.

            nun, im Zweifel kannst Du den Code von Time/Local.pm lesen und verwenden...

            Wenn ich aber irgendwelches Zeug am Perl-Interpreter ändere (den die auch voraussetzen - deshalb ist er ja bereits verfügbar) und dann läuft irgendwas von _deren_ Software nicht mehr, dann bin natürlich _ich_ schuld ... (bis zum Beweis des Gegenteils).

            Ein _neues_ Modul kann nur mit erheblicher Mühe Probleme verursachen... ;-)

            Je autarker meine add-ons sind, desto weniger besteht die Gefahr, daß ich versehentlich irgendwas kaputt mache.

            Ja.

            Die Anforderung lautet im Wesentlichen: "Die bisherige Lösung ist untauglich, und der Source-Code ist nicht verfügbar - also wegwerfen und neu schreiben."

            Eieiei, sowas kenne ich :-)

            Cheatah

  2. Hallo Michael,

    Du könntest IMHO das sehr wohl mit den Ergebnissen von gmtime() und localtime() hinbekommen. [1]
    Eigentlich brauchst Du ja nur am Beginn des Scripts die Differentz der Stunden ermitteln und dementsprechend $end_of_day korrigieren.

    Wenn localtime nicht mehr die richtige Zeit liefert, dann wird auf diesem System sowieso Hopfen und Malz verloren sein;-)

    Aber ich hab' da mal eine Gegenfrage:

    Warum verwendest Du nicht gleich die Ergebnisse von localtime() für den Datumssprung?

    Der einzige Grund könnte IMHO nur der sein, daß das ständige Berechnen der Werte mit localtime zu rechenintesiv wäre.

    Grüße
      Klaus

    [1] Allerdings ist das nicht wirklich protabel, da beispielsweise unter VMS gmtime nicht, oder auch nur nicht immer, verfügbar ist. Ich hatte einige Zeit mit VMS 6.x und Perl 5.005 zu tun, da war es nicht implemetiert, da das OS es nicht konnte. Da mußte ich dann irgendwie auf ein LOCIGAL ausweichen, oder so, weiß nicht mehr so genau.

    1. Hallo Klaus,

      Aber ich hab' da mal eine Gegenfrage:
      Warum verwendest Du nicht gleich die Ergebnisse von localtime() für den
      Datumssprung?
      Der einzige Grund könnte IMHO nur der sein, daß das ständige Berechnen
      der Werte mit localtime zu rechenintesiv wäre.

      das habe ich im Detail noch nicht ausprobiert. Müßte ich aber in der Tat mal tun - von der Software-Abhängigkeit wäre das am günstigsten.

      Im Wesentlichen läuft mein Programm als eine Art daemon, der ständig nachschaut, ob er etwas zu tun hat, und wenn nicht, dann eine Sekunde "sleep()"ed - und danach prüfen möchte, ob noch "heute" ist (denn falls nicht, ist seine Eingabequelle gerade vertrocknet und er muß sich nach einer neuen umsehen).

      Insofern wäre also ziemlich genau ein Aufruf pro Sekunde fällig, und da wäre mir time() halt schon ressourcenschonender vorgekommen als localtime(), zumal auf der Maschine noch ein Haufen anderer Sachen läuft (etwa die mySQL-Datenbank, in welche mein daemon die angenommenen Daten pumpt, und der Apache, über den sie ggf. ausgeliefert werden, und ein paar andere nette Dinge). Mein Zeug macht ansonsten schon genug Maschinenlast ...

      [1] Allerdings ist das nicht wirklich protabel, da beispielsweise
      unter VMS gmtime nicht, oder auch nur nicht immer, verfügbar ist.
      Ich hatte einige Zeit mit VMS 6.x und Perl 5.005 zu tun, da war es
      nicht implemetiert, da das OS es nicht konnte. Da mußte ich dann
      irgendwie auf ein LOCIGAL ausweichen, oder so, weiß nicht mehr so
      genau.

      Das muß es nicht sein - wenn es unter Solaris läuft, bin ich zufrieden.

      Viele Grüße
            Michael

    2. Hallo Klaus,

      Du könntest IMHO das sehr wohl mit den Ergebnissen von
      gmtime() und localtime() hinbekommen. [1]
      Eigentlich brauchst Du ja nur am Beginn des Scripts die
      Differenz der Stunden ermitteln und dementsprechend
      $end_of_day korrigieren.

      Ich muß nicht $end_of_day korrigieren, sondern $now.

      Was ich bräuchte, das wäre eine time()-Funktion im
      localtime-Universum statt im GMT-Universum ...

      Warum verwendest Du nicht gleich die Ergebnisse von
      localtime() für den Datumssprung?

      Das ist mir durch Deine Gegenfrage erst wieder klar geworden:
      Weil ich den Wert von $end_of_day auch noch für andere Dinge brauche. Der ist ein Eingabeparameter in die Funktion, die zu Beginn des neuen Tages als erstes zu laufen hat.

      Viele Grüße
            Michael

  3. Moin, Michael!

    ich bin gerade dabei, ein bißchen in Perl mit Uhrzeiten herumzurechnen.

    Was ich haben möchte, das ist eine saubere Erkennung eines Datumswechsels auf meiner Maschine (weil sich mit diesem Datumswechsel die Aufgabenstellung meines laufenden Programms ändert).

    Wenn das einzige, was du willst, die exakte Zeit der lokalen Maschine ist (und dir herzlich egal ist, wie diese Zeit gestellt wird), dann vertraue doch einfach dem externen Kommando "date", welches dir in beliebiger Formatierung sagt, wie spät es die Maschine gerade hat - und wenn's Null Uhr ist, machst du programm-mäßig eben was anderes als vorher.

    Ansonsten habe ich den Verdacht, daß die durch time() übermittelte Unix-Zeit exakt die gleiche Zeitbasis hat wie die lokale Uhr, die mit date abgefragt wird. Und das wäre doch genau das, was du brauchst, oder?

    - Sven Rautenberg

    1. Hi,

      dann vertraue doch einfach dem externen Kommando "date",

      etwas proprietär, aber bei definierter Umgebung nicht schlecht. Unter Unix hilft übrigens:

      date --date=tomorrow +%Y-%m-%d

      Cheatah

    2. Hi Sven,

      Wenn das einzige, was du willst, die exakte Zeit der lokalen Maschine
      ist (und dir herzlich egal ist, wie diese Zeit gestellt wird), dann
      vertraue doch einfach dem externen Kommando "date", welches dir in
      beliebiger Formatierung sagt, wie spät es die Maschine gerade hat -
      und wenn's Null Uhr ist, machst du programm-mäßig eben was anderes als
      vorher.

      Ich weiß nicht, wie exakt um 0 Uhr ich Gelegenheit bekomme, auf die Uhr zu sehen. Es kann sein, daß ich zu diesem Zeitpunkt gerade zu tun habe - und solange ich zu tun habe, will ich bestimmt nicht umschalten.

      Wahrscheinlich will ich ohnehin nicht genau um Mitternacht umschalten, weil ich im Moment nicht so ganz sicher bin, wann _genau_ meine Nachrichtenquelle vertrocknet und die nächste zu senden beginnt.

      Den Sendebeginn darf ich durchaus erst mal verpassen - das puffert jemand anders für mich, ich muß nur dessen Ausgabe irgendwann zu lesen anfangen. Aber vor dem Beginn der Verarbeitung der nächsten Eingabequelle habe ich ein tägliches Housekeeping zu erledigen, das eine ganze Weile dauern kann ... deshalb möchte ich damit so früh wie möglich anfangen, weil sich um diese Zeit noch ein paar andere Last-Fresser um die CPU schlagen ... und das Aufholen der Verarbeitung der nächsten Eingabe kann auch teuer werden, wenn ich mir zu viel Zeit lasse ... um zwei Stunden wie bisher darf ich mich jedenfalls nicht irren. ;-)

      Im Vergleich zu "date" (externe Abhängigkeit und zusätzlicher Prozeßstart) gefällt mir allerdings "localtime()" im Moment deutlich besser.

      Viele Grüße
            Michael

      1. Yo (und total off-topic)!

        Ich weiß nicht, wie exakt um 0 Uhr ich Gelegenheit bekomme, auf die Uhr zu sehen. Es kann sein, daß ich zu diesem Zeitpunkt gerade zu tun habe - und solange ich zu tun habe, will ich bestimmt nicht umschalten.

        Wahrscheinlich will ich ohnehin nicht genau um Mitternacht umschalten, weil ich im Moment nicht so ganz sicher bin, wann _genau_ meine Nachrichtenquelle vertrocknet und die nächste zu senden beginnt.

        Den Sendebeginn darf ich durchaus erst mal verpassen - das puffert jemand anders für mich, ich muß nur dessen Ausgabe irgendwann zu lesen anfangen.

        Das klingt für mich alles sehr nach geheimdienstlicher Tätigkeit und könnte so auch prima in Spionageromanen geschrieben stehen. Nur so als *g* zum Feiertag. ;)

        - Sven Rautenberg

  4. Moin,

    Über localtime() bekomme ich im letzten der vielen Parameter "isdst" ein Flag, das mir (laut Google - meine Perl5-Doku setzt an dieser Stelle einfach Kenntnisse des C-Compilers voraus, pfui Teufel)

    Ein  man localtime  war dir wohl zu einfach?

    --
    Henryk Plötz
    Grüße aus Berlin

    1. Hi Henryk,

      Ein  man localtime  war dir wohl zu einfach?

      um auf die Idee zu kommen, daß solche Funktionen in Perl und C tatsächlich identisch heißen, müßte ich halt mehr als fast gar nichts mehr über C wissen ... mein letztes eigenes C-Programm war in der ersten Hälfte der 90er.
      Und meine sporadischen Blicke in den Apache-Quelltext zeigen mir immer wieder, wie wenig C und (mein Subset von) Perl miteinander zu tun haben (string-Funktionen, Typ-Konvertierungen etc) ...

      In der Tat steht dort aber beispielsweise:

      The external time_t variable altzone  contains  the  differ-

      ence, in seconds, between Coordinated Universal Time and the

      alternate time zone. The external variable timezone contains

      the  difference,  in seconds, between UTC and local standard

      time. The external variable daylight indicates whether  time

      should  reflect  daylight  savings  time.  Both timezone and

      altzone default to 0 (UTC). The external  variable  daylight

      is  non-zero if an alternate time zone exists. The time zone

      names are contained in the external variable  tzname,  which

      by default is set to:

      Nur: Wie komme ich von Perl aus an solche Informationen heran?
      Über localtime() ja offenbar nicht ...

      Viele Grüße
            Michael