Python: Finde die lokale Zeitzone heraus

72

Ich möchte UTC-Zeitstempel aus einer Protokolldatei mit lokalen Zeitstempeln vergleichen. Beim Erstellen des lokalen datetimeObjekts verwende ich Folgendes:

>>> local_time=datetime.datetime(2010, 4, 27, 12, 0, 0, 0, 
                                 tzinfo=pytz.timezone('Israel'))

Ich möchte ein automatisches Werkzeug finden, das das ersetzen würdetzinfo=pytz.timezone('Israel') durch die aktuelle lokale Zeitzone ersetzt.

Irgendwelche Ideen?

Adam Matan
quelle
1
Ihr Problem ist zunächst fraglich. Je nachdem, was Sie tun, besteht eine gute Chance, dass eine Rennbedingung in der Nähe der Sommerzeitumstellung (oder einer anderen Situation, in der sich die lokale Zeitzone ändert) erzeugt wird.
Kevin
Wenn Sie mit Ortszeit Ihre Betriebssystemeinstellung meinen, können Sie sie einfach als tz-fähiges datetime-Objekt wie local_time=datetime.datetime(2010, 4, 27, 12, 0, 0, 0).astimezone()(Python> = 3.6) abrufen . [[docs]] ( docs.python.org/3/library/… )
MrFuppes

Antworten:

40

Probieren Sie dateutil aus , das einen tzlokalen Typ hat, der genau das tut, was Sie brauchen.

Steven
quelle
23
Dies ist kein Standardpaket ... gibt es kanonischere Lösungen?
Gatoatigrado
2
dateutilschlägt für einige Zeitzonen mit Daten in der Vergangenheit fehl. Und für Fälle, in denen es funktioniert, könnten Sie reine stdlib-Lösung verwenden
jfs
4
from dateutil.tz import tzlocal
Andy Hayden
2
@Jthorpe dateutilist kein Defekt und befindet sich in der aktiven Entwicklung.
Tacaswell
105

In Python 3.x kann die lokale Zeitzone folgendermaßen ermittelt werden:

import datetime
LOCAL_TIMEZONE = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo

Es ist eine heikle Verwendung von datetime‚s - Code .

Für Python> = 3.6 benötigen Sie

import datetime
LOCAL_TIMEZONE = datetime.datetime.now(datetime.timezone(datetime.timedelta(0))).astimezone().tzinfo
vbem
quelle
20
Sollte einfach datetime.datetime.now().astimezone().tzinfoschon OK sein.
Polv
3
@Polv datetime.datetime.utcnow().astimezone().tzinfoscheint auch korrekt zu sein und ist für mich leichter zu lesen, da ausdrücklich angegeben wird, dass zunächst UTC verwendet wird. Da no datetimeanfangs an eine Zeitzone gebunden ist, sollte es intern keinen Unterschied machen.
sjngm
8
@Polv @sjngm nicht ganz, da das nur für Python> = 3.6 funktioniert. Sie können keine astimezone()naiven Daten vor 3.6 aufrufen.
Fynn Becker
1
Clever, aber funktioniert es, wenn Ihr Programm langfristig ausgeführt wird, beispielsweise durch eine Sommerzeitänderung?
Wilmer
Diese Codezeile gibt UTC auf meinem Python-Terminal zurück (in einem Docker-Container?), Aber ich bin in GMT + 4
Jose Georges
31

um UTC-Zeitstempel aus einer Protokolldatei mit lokalen Zeitstempeln zu vergleichen.

Es ist schwierig, den Namen Olson TZ für eine lokale Zeitzone auf tragbare Weise herauszufinden . Glücklicherweise benötigen Sie es nicht, um den Vergleich durchzuführen.

tzlocalDas Modul gibt eine Pytz-Zeitzone zurück, die der lokalen Zeitzone entspricht:

from datetime import datetime

import pytz # $ pip install pytz
from tzlocal import get_localzone # $ pip install tzlocal

tz = get_localzone()
local_dt = tz.localize(datetime(2010, 4, 27, 12, 0, 0, 0), is_dst=None)
utc_dt = local_dt.astimezone(pytz.utc) #NOTE: utc.normalize() is unnecessary here

Im Gegensatz zu anderen bisher vorgestellten Lösungen vermeidet der obige Code die folgenden Probleme:

  • Ortszeit kann mehrdeutig sein dh ein genauer Vergleich kann für einige Ortszeiten unmöglich sein
  • Der utc-Offset kann für denselben lokalen Zeitzonennamen für Daten in der Vergangenheit unterschiedlich sein. Einige Bibliotheken, die zeitzonenbezogene Datetime-Objekte unterstützen (z. B. dateutil), berücksichtigen dies nicht

Hinweis: Um ein zeitzonenbewusstes datetime-Objekt von einem naiven datetime-Objekt abzurufen, sollten Sie * verwenden :

local_dt = tz.localize(datetime(2010, 4, 27, 12, 0, 0, 0), is_dst=None)

Anstatt von:

#XXX fails for some timezones
local_dt = datetime(2010, 4, 27, 12, 0, 0, 0, tzinfo=tz)

* *is_dst=None erzwingt eine Ausnahme, wenn die angegebene Ortszeit nicht eindeutig oder nicht vorhanden ist.

Wenn Sie sicher sind, dass alle lokalen Zeitstempel denselben (aktuellen) utc-Offset für die lokale Zeitzone verwenden, können Sie den Vergleich nur mit stdlib durchführen:

# convert a naive datetime object that represents time in local timezone to epoch time
timestamp1 = (datetime(2010, 4, 27, 12, 0, 0, 0) - datetime.fromtimestamp(0)).total_seconds()

# convert a naive datetime object that represents time in UTC to epoch time
timestamp2 = (datetime(2010, 4, 27, 9, 0) - datetime.utcfromtimestamp(0)).total_seconds()

timestamp1 und timestamp2 kann direkt verglichen werden.

Hinweis:

  • timestamp1Die Formel funktioniert nur, wenn der UTC-Offset bei epoch ( datetime.fromtimestamp(0)) derselbe ist wie jetzt
  • fromtimestamp() Erstellt ein naives Datum / Uhrzeit-Objekt in der aktuellen lokalen Zeitzone
  • utcfromtimestamp() Erstellt ein naives Datum / Uhrzeit-Objekt in UTC.
jfs
quelle
Genau das, was ich brauchte!
Nagylzs
Ich verstehe nicht, warum das Abrufen der lokalen Zeitzone nicht in einem der Python-Kernmodule enthalten ist ...
Rfraile
@Rfraile: Wenn wir Eckfälle ausschließen, in denen Sie eine pytzZeitzone benötigen würden , hatte Python seit Ewigkeiten eine lokale Zeitzone in stdlib. 2- PEP 615 - Unterstützung für die IANA-Zeitzonendatenbank in der Standardbibliothek wird in Python 3.9 akzeptiert (es bietet Zugriff auf dieselben tzdata wie pytz)
jfs
@jfs Ich wollte Unterstützung für Olson Zeitzone sagen, sorry. Gut zu wissen, dass dieser neue PEP 615 einen großen Mehrwert bietet, danke für das Teilen
Rfraile
19

Ich habe mir dasselbe gefragt und die Antwort in 1 gefunden:

Schauen Sie sich Abschnitt 8.1.7 an: das Format "% z" (Kleinbuchstaben, der Großbuchstabe Z gibt auch die Zeitzone zurück, jedoch nicht im 4-stelligen Format, sondern in Form von Zeitzonenabkürzungen, wie in [3]). of strftime gibt die in E-Mail-Headern standardmäßige Form "+/- 4DIGIT" zurück (siehe Abschnitt 3.3 von RFC 2822, siehe [2], in dem die anderen Möglichkeiten zur Angabe der Zeitzone für E-Mail-Header überholt sind).

Wenn Sie also Ihre Zeitzone in diesem Format haben möchten, verwenden Sie:

time.strftime("%z")

[1] http://docs.python.org/2/library/datetime.html

[2] http://tools.ietf.org/html/rfc2822#section-3.3

[3] Zeitzonenabkürzungen: http://en.wikipedia.org/wiki/List_of_time_zone_abbreviations , nur als Referenz.

David L.
quelle
2
Bei der Frage geht es darum, ein tzinfoObjekt zu finden, das der lokalen Zeitzone entspricht, und nicht den aktuellen utc-Offset als Zeichenfolge. time.timezone,.altzone Geben Sie aktuelle UTC - Offset. Zeitzonenversatz oder Abkürzungen sind nicht eindeutig. Es ist nicht so einfach, eine lokale Zeitzone zu erhalten, die Sie für Daten in der fernen Vergangenheit, Gegenwart und nahen Zukunft verwenden können. Sehen Sie sich tzlocalden Quellcode des Moduls an, um ein Beispiel zu sehen, wie dies durchgeführt werden kann.
JFS
Einfach und effektiv
Mohammad Banisaeid
6

Holen Sie sich zuerst Pytz- und Tzlocal-Module

pip install pytz tzlocal

dann

from tzlocal import get_localzone
local = get_localzone()

dann kannst du Dinge wie tun

from datetime import datetime
print(datetime.now(local))
Aminalid
quelle
5

Hier ist eine Möglichkeit, die lokale Zeitzone nur mit der Standardbibliothek abzurufen (funktioniert nur in einer * nix-Umgebung):

>>> '/'.join(os.path.realpath('/etc/localtime').split('/')[-2:])
'Australia/Sydney'

Sie können dies verwenden, um eine pytzZeitzone zu erstellen :

>>> import pytz
>>> my_tz_name = '/'.join(os.path.realpath('/etc/localtime').split('/')[-2:])
>>> my_tz = pytz.timezone(my_tz_name)
>>> my_tz
<DstTzInfo 'Australia/Sydney' LMT+10:05:00 STD>

... die Sie dann anwenden können auf datetime:

>>> import datetime
>>> now = datetime.datetime.now()
>>> now
datetime.datetime(2014, 9, 3, 9, 23, 24, 139059)

>>> now.replace(tzinfo=my_tz)
>>> now
datetime.datetime(2014, 9, 3, 9, 23, 24, 139059, tzinfo=<DstTzInfo 'Australia/Sydney' LMT+10:05:00 STD>)
amorph
quelle
Um zu vermeiden, quadratische Räder zu erfinden, schauen Sie sich den tzlocalQuellcode an
jfs
Dadurch wird die Zeitzone der Umgebung mit der TZ-Variablen überschrieben.
Karsten
Existiert '/ etc / localtime' unter MacOS?
Lucas
Oder unter Windows? ;-)
quant_dev
5

Folgendes scheint für 3.7+ mit Standardbibliotheken zu funktionieren:

from datetime import timedelta
from datetime import timezone
import time

def currenttz():
    if time.daylight:
        return timezone(timedelta(seconds=-time.altzone),time.tzname[1])
    else:
        return timezone(timedelta(seconds=-time.timezone),time.tzname[0])
Robbie Matthews
quelle
4

Vermeiden von nicht standardmäßigen Modulen (scheint eine fehlende Methode des datetime-Moduls zu sein):

from datetime import datetime
utcOffset_min = int(round((datetime.now() - datetime.utcnow()).total_seconds())) / 60   # round for taking time twice
utcOffset_h = utcOffset_min / 60
assert(utcOffset_min == utcOffset_h * 60)   # we do not handle 1/2 h timezone offsets

print 'Local time offset is %i h to UTC.' % (utcOffset_h)
Thoku
quelle
2
Wenn Sie sich in der Vergangenheit nicht für DST- oder UTC-Offsets interessieren (wie Ihre Lösung zeigt); Sie könnten nur verwenden -time.timezone.
JFS
4

Basierend auf der Antwort von JF Sebastian können Sie dies mit der Standardbibliothek tun:

import time, datetime
local_timezone = datetime.timezone(datetime.timedelta(seconds=-time.timezone))

Getestet in 3.4, sollte auf 3.4+ funktionieren

Gilly
quelle
Ich denke, dies kann zu falschen Ergebnissen führen, wenn die Sommerzeit wirksam ist.
Preston Landers
3

Hier ist eine etwas präzisere Version der Lösung von @ vbem:

from datetime import datetime as dt

dt.utcnow().astimezone().tzinfo

Der einzige wesentliche Unterschied ist , dass ich ersetzt datetime.datetime.now(datetime.timezone.utc)mit datetime.datetime.utcnow(). Der Kürze halber habe ich auch datetime.datetimeals aliasiert dt.

Für meine Zwecke möchte ich den UTC-Offset in Sekunden. So sieht das aus:

dt.utcnow().astimezone().utcoffset().total_seconds()
David Marx
quelle
1
ValueError: astimezone () kann nicht auf eine naive datetime (python 3.4)
angewendet werden
1
Ja, nachdem ich Code mit dieser Lösung veröffentlicht hatte, meldeten Benutzer, die Linux verwendeten, denselben Fehler. Wenn ich mich richtig erinnere, ist die Hauptursache, dass die Betriebssystemzeit nicht für eine Zeitzone konfiguriert ist (dh "naive" UTC), und dies bricht den Code. Ich habe nicht nach einer Lösung gesucht, die über das Umschließen dieses Codes in einen Try / Except-Block hinausgeht.
David Marx
OK danke. Seitdem habe ich die Idee einer reinen stdlib Lösung auf und ging mit pytzund tzlocal. Ich bin vorsichtig, wenn ich Abhängigkeiten von Drittanbietern hinzufüge, wenn Python bereits mit so viel ausgeliefert wird, aber wenn Sie sich mit Zeitzonen befassen, benötigen Sie eindeutig externe Hilfe.
Pbarill
2

Basierend auf der obigen Antwort von Thoku ist hier eine Antwort, die die Zeitzone auf die nächste halbe Stunde auflöst (was für einige Zeitzonen relevant ist, z. B. für Südaustralien):

from datetime import datetime
round((round((datetime.now()-datetime.utcnow()).total_seconds())/1800)/2)
user2380550
quelle
Python 3 Version:round((round((datetime.datetime.now()-datetime.datetime.utcnow()).total_seconds())/1800)/2)
Dmitry
(1) Es ist falsch, wenn der lokale UTC-Offset in der Vergangenheit unterschiedlich war (in vielen Zeitzonen). (2) Es round()kann hier (für einige Zeitzonen) einen falschen UTC-Offset erzeugen, und dies ist nicht erforderlich. Informationen
jfs
2

Ich habe auch nach einer einfachen Möglichkeit gesucht, die lokale Hostkonfiguration zu lesen und die zeitzonenbezogene local_time darauf basierend zu erhalten. Ab Python 3.6+ ist der einfachste Ansatz die Verwendung von dateutil.tz, das gelesen wird/etc/localtime zeitzonenbezogene Datum / Uhrzeit-Objekt und unterstützt wird.

Hier finden Sie weitere Informationen dazu: https://dateutil.readthedocs.io/en/stable/tz.html

Die Implementierung, um das zu erreichen, wonach Sie suchen, lautet wie folgt:

from datetime import datetime
from dateutil import tz
local_time = datetime.now(tz.gettz())

Dadurch erhalten Sie die folgende local_time:

2019-10-18 13:41:06.624536-05:00

Zusätzliche Ressourcen, die ich bei der Untersuchung dieses Themas verwendet habe: Paul Ganssle Präsentation zu Zeitzonen: https://www.youtube.com/watch?v=l4UCKCo9FWY

pytz: Die schnellste Fußwaffe im Westen https://blog.ganssle.io/articles/2018/03/pytz-fastest-footgun.html

darekm101
quelle
1

Für einfache Dinge kann die folgende tzinfoImplementierung verwendet werden, die das Betriebssystem nach Zeitzonenversätzen abfragt:

import datetime
import time

class LocalTZ(datetime.tzinfo):
    _unixEpochOrdinal = datetime.datetime.utcfromtimestamp(0).toordinal()

    def dst(self, dt):
        return datetime.timedelta(0)

    def utcoffset(self, dt):
        t = (dt.toordinal() - self._unixEpochOrdinal)*86400 + dt.hour*3600 + dt.minute*60 + dt.second + time.timezone
        utc = datetime.datetime(*time.gmtime(t)[:6])
        local = datetime.datetime(*time.localtime(t)[:6])
        return local - utc


print datetime.datetime.now(LocalTZ())
print datetime.datetime(2010, 4, 27, 12, 0, 0, tzinfo=LocalTZ())

# If you're in the EU, the following datetimes are right on the DST change.
print datetime.datetime(2013, 3, 31, 0, 59, 59, tzinfo=LocalTZ())
print datetime.datetime(2013, 3, 31, 1, 0, 0, tzinfo=LocalTZ())
print datetime.datetime(2013, 3, 31, 1, 59, 59, tzinfo=LocalTZ())

# The following datetime is invalid, as the clock moves directly from
# 01:59:59 standard time to 03:00:00 daylight savings time.
print datetime.datetime(2013, 3, 31, 2, 0, 0, tzinfo=LocalTZ())

print datetime.datetime(2013, 10, 27, 0, 59, 59, tzinfo=LocalTZ())
print datetime.datetime(2013, 10, 27, 1, 0, 0, tzinfo=LocalTZ())
print datetime.datetime(2013, 10, 27, 1, 59, 59, tzinfo=LocalTZ())

# The following datetime is ambigous, as 02:00 can be either DST or standard
# time. (It is interpreted as standard time.)
print datetime.datetime(2013, 10, 27, 2, 0, 0, tzinfo=LocalTZ())
Søren Løvborg
quelle
(1) Es kann nicht funktionieren, wenn das timeModul auf einer bestimmten Plattform keinen Zugriff auf die tz-Datenbank hat (Hinweis: pytzIn der Frage wird ein solcher Zugriff auf tragbare Weise tbereitgestellt ). (2) Die Formel ist falsch, wenn der entsprechende utc-Offset dtnicht lautet -time.timezoneund selbst wenn das timeModul das Richtige kennt, localtime(t)fragt Ihr Code möglicherweise nach einem falschen Wert (berücksichtigen Sie die Zeit um einen Sommerzeitübergang).
JFS
1
now_dt = datetime.datetime.now()
utc_now = datetime.datetime.utcnow()
now_ts, utc_ts = map(time.mktime, map(datetime.datetime.timetuple, (now_dt, utc_now)))
offset = int((now_ts - utc_ts) / 3600)

hoffe das wird dir helfen.

宝 城 p
quelle
1

Sie können mit Pendel glücklich sein

>>> pendulum.datetime(2015, 2, 5, tz='local').timezone.name
'Israel'

Pendel hat eine gut gestaltete API zum Bearbeiten von Daten. Alles ist TZ-bewusst.

Étienne Bersac
quelle
1

Ich möchte UTC-Zeitstempel aus einer Protokolldatei mit lokalen Zeitstempeln vergleichen

Wenn dies Ihre Absicht ist, würde ich mir keine Gedanken darüber machen, bestimmte tzinfo-Parameter oder zusätzliche externe Bibliotheken anzugeben. Seit Python 3.5 ist das integrierte Datum / Uhrzeit-Modul alles, was Sie benötigen, um automatisch eine UTC und einen lokalen Zeitstempel zu erstellen.

import datetime
f = "%a %b %d %H:%M:%S %Z %Y"         # Full format with timezone

# tzinfo=None
cdatetime = datetime.datetime(2010, 4, 27, 12, 0, 0, 0)  # 1. Your example from log
cdatetime = datetime.datetime.now()   # 2. Basic date creation (default: local time)
print(cdatetime.strftime(f))          # no timezone printed
# Tue Apr 27 12:00:00  2010

utctimestamp = cdatetime.astimezone(tz=datetime.timezone.utc)  # 1. convert to UTC
utctimestamp = datetime.datetime.now(tz=datetime.timezone.utc) # 2. create in UTC
print(utctimestamp.strftime(f))
# Tue Apr 27 17:00:00 UTC 2010

localtimestamp = cdatetime.astimezone()               # 1. convert to local [default]
localtimestamp = datetime.datetime.now().astimezone()  # 2. create with local timezone
print(localtimestamp.strftime(f))
# Tue Apr 27 12:00:00 CDT 2010

Der Parameter '% Z' von datetime.strftime () druckt das Zeitzonenakronym in den Zeitstempel, den Menschen lesen können.

codejedi365
quelle
0

Beachten Sie zunächst, dass die Frage eine falsche Initialisierung eines bewussten Datetime-Objekts darstellt:

>>> local_time=datetime.datetime(2010, 4, 27, 12, 0, 0, 0,
...                                  tzinfo=pytz.timezone('Israel'))

erstellt eine ungültige Instanz. Man kann das Problem erkennen, indem man den UTC-Offset des resultierenden Objekts berechnet:

>>> print(local_time.utcoffset())
2:21:00

(Beachten Sie das Ergebnis, das ein ungerader Bruchteil einer Stunde ist.)

Um eine bewusste Datumszeit mit Pytz richtig zu initialisieren, sollte die folgende localize()Methode angewendet werden:

>>> local_time=pytz.timezone('Israel').localize(datetime.datetime(2010, 4, 27, 12))
>>> print(local_time.utcoffset())
3:00:00

Wenn Sie nun eine lokale Pytz-Zeitzone als neues tzinfo benötigen, sollten Sie das tzlocal-Paket verwenden, wie andere erklärt haben. Wenn Sie jedoch nur eine Instanz mit einem korrekten lokalen Zeitzonenversatz und einer korrekten Abkürzung benötigen, können Sie mit Python 3.3 starten Rufen Sie die astimezone()Methode ohne Argumente auf, um eine bewusste datetimeInstanz in Ihre lokale Zeitzone zu konvertieren :

>>> local_time.astimezone().strftime('%Y-%m-%d %H:%M %Z %z')
'2010-04-27 05:00 EDT -0400'
Alexander Belopolsky
quelle
(1) pytzÜbergeben datetime()Sie die Zeitzone nicht direkt an den Konstruktor - wenn Sie wissen, dass Sie dies nicht tun sollten; warum machst du das (2) Wenn Sie pytzinstalliert haben; Warum verwenden Sie nicht portable astimezone()Geräte, die auf einer bestimmten Plattform möglicherweise keinen Zugriff auf die tz-Datenbank haben? Sie könnten ein reines Python- tzlocalModul erhalten, das pytzstattdessen ein tzinfo-Objekt für die lokale Zeitzone unter Windows und Unix ( /etc/localtimeoder /usr/local/etc/localtime) findet.
JFS
(1) weil OP auf diese Weise seine Frage gestellt hat; (2) astimezone () ist so portabel wie es nur geht und kann verwendet werden, ohne dass ein zusätzliches Paket installiert werden muss.
Alexander Belopolsky
(1) Die Frage kann einen fehlerhaften Code enthalten. Die Antwort sollte nicht (ohne guten Grund und eindeutige Angabe im Codeblock selbst, Beispiel ). (2) Der Code verwendet bereits Pytz. Die richtige Antwort sollte die tz-Datenbank verwenden.
JFS
tzlocal gehört nicht zu pytz. Wenn es auf dem System des Benutzers verfügbar ist, ist die Verwendung eine gute Lösung. Wenn nicht, ist astimezone () die empfohlene Lösung für Python 3.3 und höher. Ob die "richtige" Lösung die tz-Datenbank verwenden soll oder nicht, hängt von den Benutzeranforderungen ab. Hoffentlich ist das Ergebnis für die meisten Benutzer unabhängig davon gleich. Beachten Sie, dass das Mischen von Pytz-Zeitzonen mit beispielsweise Datumsangaben nicht empfohlen wird. Datetime-Instanzen, die die Standardbibliothek datetime.timezone verwenden, sind jedoch vollständig mit denen kompatibel, die Pytz verwenden.
Alexander Belopolsky
(1) Die Frage kann einen fehlerhaften Code enthalten. Die Antwort sollte nicht ... Das ist ein guter Rat. Ich habe meine Antwort umgeschrieben. Vielen Dank.
Alexander Belopolsky
-1

tzlocal von dateutil .

Es folgt ein Codebeispiel. Letzte Zeichenfolge, die zur Verwendung in Dateinamen geeignet ist.

>>> from datetime import datetime
>>> from dateutil.tz import tzlocal
>>> str(datetime.now(tzlocal()))
'2015-04-01 11:19:47.980883-07:00'
>>> str(datetime.now(tzlocal())).replace(' ','-').replace(':','').replace('.','-')
'2015-04-01-111947-981879-0700'
>>> 
Johnny Utahh
quelle