getmtime () vs datetime.now ():

8

Dieser Code gibt einmal pro Jahr in der Nacht der Uhrverschiebung (mitteleuropäische Sommerzeit bis mitteleuropäische Zeit) eine falsche Warnung aus:

import os
import datetime

now = datetime.datetime.now()
age = now - datetime.datetime.fromtimestamp(os.path.getmtime(file_name))
if (age.seconds + age.days * 24 * 3600) < -180:
    print('WARN: file has timestap from future?: %s' % age)

Wie kann dieser Code auch während dieser jährlichen einstündigen Schicht funktionieren?

Aktualisieren

Ich kümmere mich nur um das Alter, nicht um die Uhrzeit.

guettli
quelle

Antworten:

6

Das gepostete Fragment kann leicht verbessert werden, indem von der lokalen zur UTC-Zeit gewechselt wird. In UTC gibt es keine Änderungen der Sommerzeit (Sommerzeit). Ersetzen Sie einfach diese beiden Datums- / Uhrzeitfunktionen now()-> utcnow()( Dokumente ) und fromtimestamp()-> utcfromtimestamp()( Dokumente ).

Wenn die einzige erwartete Ausgabe jedoch ein Dateialter in Sekunden ist, können wir die Zeitstempel (Sekunden aus einer "Epoche") ohne Konvertierung direkt verwenden:

import time
import os.path

...
age = time.time() - os.path.getmtime(file_name)
VPfB
quelle
3
Die Verwendung von UTC ist in erster Linie der allgemein richtige Ansatz.
konstantin
@konstantin warum ist der richtige Ansatz? Ich mag diese einfache Lösung, da ich mich (in diesem Zusammenhang) nur um das Alter (timedelta) kümmere, nicht um die datetime.
Guettli
@guettli, ich würde sagen, dies ist wahrscheinlich die beste und einfachste Antwort für Ihren Anwendungsfall. Das Wichtigste beim Vergleichen der Zeit ist, dass Sie Gleiches für Gleiches vergleichen. In diesem Beispiel handelt es sich um einen UTC-Zeitstempel mit einem UTC-Zeitstempel, sodass dies immer funktioniert. Der Grund, warum Ihr Code ursprünglich nicht funktioniert hat, ist, dass Sie Objekte verglichen haben, die für einen UTC-Zeitstempel keine Relevanz mehr hatten, da sie nicht zeitzonenbewusst sind. Wenn Sie komplexere Dinge tun möchten, ist meine Antwort möglicherweise nützlicher, da es einfacher ist, mit Datetime-Objekten zu arbeiten, aber für einen einfachen Vergleich funktioniert dies.
KillerKode
1
@guettli Ich habe es als "unversal richtigen Ansatz" bezeichnet, weil ich zu viele Stunden, wenn nicht Tage damit verbracht habe, Systeme und Schnittstellen zu debuggen, die nur mit einigen a priori-Annahmen über Datums- und Zeitzonen funktionierten, die als Eingabe empfangen wurden. Wenn z. B. Ihr Server nicht in derselben Zeitzone wie der Client ausgeführt wird und die Datumsangaben ohne expliziten UTC-Offset weitergegeben und als lokale Datumsangaben interpretiert werden, funktionieren die Dinge möglicherweise immer noch irgendwie (z. B. bei der Berechnung von Deltas), aber das Debuggen ist möglicherweise schwierig vermieden, wenn alle nur an erster Stelle / so schnell wie möglich bei UTC blieben.
Konstantin
1
@guettli Danke, dass du meine Antwort angenommen hast. Ich hoffe, es war hilfreich, weil ich ein bisschen Angst habe, dass meine kurze Antwort keine so großzügige Prämie wert ist und Sie mich überbezahlt haben. Mit freundlichen Grüßen (Schöne Grüße nach Chemnitz)
VPfB
3

Ihre beiden datetime-Objekte sind 'naiv', was bedeutet, dass sie nichts über die Sommerzeit wissen. datetime.now()Gibt die aktuelle Zeit zurück, auf der Ihr Computer ausgeführt wird. Dazu gehört möglicherweise die Sommerzeit. Gleiches gilt für datetime.fromtimestamp(os.path.getmtime()).

# 1 - Das Lokalisieren Ihrer datetime-Objekte könnte eine Option sein. etwas wie

from datetime import datetime
import tzlocal
now_aware = tzlocal.get_localzone().localize(datetime.now())
file_mtime = datetime.fromtimestamp(os.path.getmtime(file))
# assuming the file was created on a machine in the same timezone (!):
file_mtime_aware = now_aware.tzinfo.localize(file_mtime)
age = now_aware - file_mtime_aware

# 2 - eine weitere Option mit UTC-Konvertierung mit datetime:

now = datetime.utcnow()
age = now - datetime.utcfromtimestamp(os.path.getmtime(file_name))
if (age.seconds + age.days * 24 * 3600) < -180:
    print(f'WARN: file has timestamp from future?: {age} s')

# 3 - gibt, wie VPfB in seiner Antwort hervorhebt , os.path.getmtimeeinen UTC-Zeitstempel zurück ( siehe OS- Modul-Dokumente und Zeitmodul- Dokumente ). Die einfachste Lösung könnte daher darin bestehen, die Konvertierung zunächst zu überspringen datetimeund nur UTC-Zeitstempel zu verwenden. zB den aktuellen UTC-Zeitstempel abrufen als time.time().

Das Arbeiten mit Zeitzonen kann Sie verrückt machen ... aber es gibt einige gute Ressourcen, z . B. diesen mittleren Beitrag .

MrFuppes
quelle
1

Ihr Problem ist, dass Sie Ihre Zeit bekommen, ohne dass Sie sich der Zeitzone bewusst sind. Wenn sich also die Uhren ändern, beenden Sie den Vergleich eines Zeitstempels vor dem Uhrenwechsel mit einem anderen nach dem Uhrenwechsel, und Ihr Code sieht dies nicht.

Sie sollten stattdessen festlegen, dass Ihre datetime-Objekte auf einer bestimmten Zeitzone basieren, damit Sie keine Probleme mit dem Ändern der Uhr haben. Ich empfehle, das pytz-Modul zu verwenden, um Ihnen dabei zu helfen. In dieser Antwort sehen Sie eine Liste der verfügbaren Zeitzonen: Gibt es eine Liste der Pytz-Zeitzonen?

Hier ist ein einfaches Codebeispiel, wie Sie dies mit zeitzonensensitiven Objekten tun können:

import os
from datetime import datetime
import pytz


def get_datetime_now(timezone):
    """
    Returns timezone aware datetime object for right now
    """
    if timezone not in pytz.all_timezones:
        return None
    tz = pytz.timezone(timezone)
    dt = datetime.now().astimezone()
    return dt.astimezone(tz)


def timestamp_to_datetime(timestamp, timezone):
    """
    Returns a datetime object from a timestamp
    """
    if timezone not in pytz.all_timezones:
        return None
    tz = pytz.timezone(timezone)
    dt = datetime.fromtimestamp(timestamp).astimezone()
    return dt.astimezone(tz)


timezone = 'CET'

file_timestamp = os.path.getmtime(file_name)

now = get_datetime_now(timezone)
file_datetime = timestamp_to_datetime(file_timestamp, timezone)
age = now - file_datetime

if (age.seconds + age.days * 24 * 3600) < -180:
    print('WARN: file has timestap from future?: %s' % age)
KillerKode
quelle
Warum ist Ihre Lösung besser als age = time.time() - os.path.getmtime(file_name). Ich interessiere mich nur für das Alter (Zeitdelta), nicht für das Datum / die Uhrzeit.
Guettli
1
Wenn Sie sich nur für das Zeitdelta interessieren, ist dies nicht der Fall. Der Grund, warum ich so vorgegangen bin, ist, dass Sie erwähnt haben, dass es in der CET-Zeitzone liegt und gezeigt haben, dass Sie mit datetime-Objekten arbeiten. Dieser Ansatz kann nützlich sein, wenn Sie Zeiten zwischen zwei verschiedenen Zeitzonen vergleichen. Wenn Ihre Zeitzonen identisch sind, sollte es ausreichen, nur die Zeitstempel zu vergleichen. Die einzige andere Überlegung besteht darin, sicherzustellen, dass Ihre Systemzeit mit einem NTP-Server synchronisiert ist.
KillerKode