[Python3] Eine Geschichte, die bei der Zeitzonenkonvertierung steckt

Ich habe ein kleines Tool geschrieben, das Informationen einschließlich der Zeit von einer externen API empfängt und Slack benachrichtigt, aber ich konnte die Zeitzone nicht konsistent und stabil konvertieren, was bedeutet. Notieren Sie sich, was Sie während der Umfrage gelernt haben und welche Lösungen möglich sind.

Zusammenfassung

Komplizenschaft der Zeitzonenverarbeitung in Python × Pytz

In meinem Fall gab es verschiedene Arten von Stolpersteinen.

  1. Timezeon aware / Offest native
  2. Pytz-Strenge
  3. Pytz-Spezifikationen

Abschließend zeige ich Ihnen, was ich über diese recherchiert habe und welchen Code ich geschrieben habe, um sie zu überwinden.

Timezeon aware / Offest native

Es gibt die beiden oben genannten Datums- / Uhrzeittypen.

Timezone Aware ist eine Datums- und Uhrzeitangabe mit einer explizit angegebenen Zeitzone, und Offset native ist eine Zeit ohne Angabe einer Zeitzone. Die beiden können nicht verglichen werden.

Diese müssen unterschieden werden.

import os
from datetime import datetime
import pytz

# Offest native
d1 = datetime.utcnow()                                                                                                        
print(d1)
>>> 2020-04-19 10:07:47.885883

# Timezone aware
d2 = pytz.utc.localize(d1)
print(d2)
>>> 2020-04-19 10:07:47.885883+00:00

#Die beiden können nicht verglichen werden
d1 < d2
# >>> TypeError: can't compare offset-naive and offset-aware datetimes

Strenge Pytz

Die Zeitzone scheint durch den Ort und die ** Zeit ** bestimmt zu sein. Es scheint einen Fall zu geben, in dem die Definition des Zeitunterschieds aufgrund historischer Umstände geändert wird, und unser Japan entspricht auch diesem Beispiel.

Es scheint, dass der Zeitunterschied zwischen Tokio bis 1888 und danach unterschiedlich ist, und vor 1888 scheint er ** + 09: 19 ** von UTC entfernt zu sein. Pytz berücksichtigt diese strikte Überlegung, und es scheint, dass dies bei einigen Methoden des Zeitzonenumwandlungssystems (datetime.replace usw.) berücksichtigt wird.

import pytz

#Hier ANZEIGE_TIMEZONE='Asia/Tokyo'Annehmen
DISPLAY_TIMEZONE = os.environ.get('DISPLAY_TIMEZONE') 
tz = pytz.timezone(DISPLAY_TIMEZONE)

tz                                                                                                                                              
>>> <DstTzInfo 'Asia/Tokyo' LMT+9:19:00 STD>

Es ist 19 Minuten weg. Dies scheint jedoch aufgrund der ordnungsgemäßen Einhaltung korrekt zu sein.

Wenn Ihr Programm ** Offset native ** behandelt, können Sie timezone.localize () verwenden, die vom Zeitzonenobjekt von pytz bereitgestellt wird. Es wird um +09: 00 richtig konvertiert.

** Solange Sie die pytz-Methode "Lokalisieren" verwenden, erfolgt die Zeitzonenumwandlung wie beabsichtigt um +09: 00, sodass Sie sich über diese Abweichung keine Gedanken machen müssen. ** ** **

tz.localize(d1)                                                                                                                                 
>>> datetime.datetime(2020, 4, 19, 10, 20, 3, 201190, tzinfo=<DstTzInfo 'Asia/Tokyo' JST+9:00:00 STD>)

Pytz-Spezifikationen

pytz 'localize ()' akzeptiert keine zeitzonenabhängigen Zeitzonen. Dieser Punkt braucht Aufmerksamkeit.

# localize offset ative
tz.localize(d1)
>>> datetime.datetime(2020, 4, 19, 10, 7, 47, 885883, tzinfo=<DstTzInfo 'Asia/Tokyo' JST+9:00:00 STD>)

# localize timezone aware (error)
tz.localize(d2)
# >>> ValueError: Not naive datetime (tzinfo is already set)

Wenn Sie sich also mit ** zeitzonenbewusster Zeit befassen, kann localize nicht so verwendet werden, wie es ist, und es ist erforderlich, es einmal in nativen Offset zu konvertieren. ** Dies tritt häufig auf, wenn die zurückgegebene Zeit, z. B. die Verwendung eines Wrappers für eine externe API, zeitzonenabhängig ist.

Der hier geschriebene Inhalt erwähnt Pytz-Spezifikationen haben sich geändert | Qiita @ higitune, daher sollten Sie dort einen Blick darauf werfen. .. Es wird sehr hilfreich sein, einschließlich des Kommentarbereichs.

Konvertieren Sie einen zeitzonenabhängigen Datum / Uhrzeit-Typ in eine andere Zeitzone

Wenn Sie ein Paket verwenden, das von einer anderen Person als Ihnen entwickelt wurde, ist es Sache des Implementierers, ob die vom Paketmodul zurückgegebene Zeit zeitzonenabhängig ist oder nativ versetzt ist. Um sie zu kombinieren und Ihre eigenen Entwicklungsanforderungen zu implementieren, müssen Sie eine Mischung aus diesen verwalten.

Daher ist es besser, es einmal auf dem Zeitstempel abzulegen (obwohl es sich in der Theory Street befindet). Mit dem folgenden Code können Sie JST sowohl Offset-native als auch zeitzonenabhängig konvertieren. (Es ist unbestätigt, ob ähnliche Ergebnisse in anderen Ländern erzielt werden können.)

import pytz
from datetime import datetime

DISPLAY_TIMEZONE = 'Asia/Tokyo'
tz = pytz.timezone(DISPLAY_TIMEZONE)

def localized_datetime(date: datetime):
    return datetime.fromtimestamp(date.timestamp(), tz=tz)


if __name__ == '__main__':
    # d1: native datetime
    d1 = datetime.now()
    print(f"d1: {d1}")

    # d2: utc localize
    d2 = tz.localize(d1)
    print(f"d2: {d2}")

    print(localized_datetime(d1))
    print(localized_datetime(d2))

>>> d1: 2020-04-19 20:15:30.974272
>>> d2: 2020-04-19 20:15:30.974272+09:00
>>> 2020-04-19 20:15:30.974272+09:00
>>> 2020-04-19 20:15:30.974272+09:00

Zusammenfassung

Die Zeitzone ist verwirrend ...

Der obige Code wird als Referenzimplementierung veröffentlicht, aber es scheint, dass die tatsächliche lokalisierte Zeit hauptsächlich für die Präsentationsschicht (dh das Erscheinungsbild) erforderlich ist, also in der Logikschicht und der Persistenzschicht so weit wie möglich mit datetime Ich denke, es wäre besser, stattdessen einen Zeitstempel zu verwenden.

Die Datums- und Uhrzeitangabe ist jedoch beim Schreiben von Code überwiegend bequemer, und ich denke, es gibt viele Fälle, in denen Datumsoperationen in der Logikschicht häufig ausgeführt werden müssen.

In einem solchen Fall halte ich es für besser, zu vereinheitlichen, welche der Zeitzonen awre / offset nativ verwendet wird, zumindest in dem Code, der näher an der von mir selbst geschriebenen Logikschicht liegt. Wenn die von externen Paketen verarbeiteten Zeiten inkonsistent sind, können Sie eine eigene Ebene erstellen, die die zu verwendende Schnittstelle dünn umschließt und die Inkonsistenz in Bezug auf Datums- und Uhrzeittyp und Zeitzone absorbiert. Ich denke, die Hauptlogik wird sauberer sein.

Ich kann mich nach meinem derzeitigen Kenntnisstand nicht entscheiden, an welche ich senden soll. Ich würde mich freuen, wenn Sie uns Ihre Meinung im Kommentarbereich mitteilen könnten.

Der obige Code enthält auch subtile Fallstricke. Zum Beispiel

"Der von der Funktion / Methode zurückgegebene Datum / Uhrzeit-Typ ist nativ versetzt, die Zeit basiert jedoch auf UTC."

In einem solchen Fall. Wenn die API des externen Dienstes so konzipiert ist, dass UTC konsistent zurückgegeben wird, und die externe Paketimplementierung, die sie umschließt, nicht cool ist und unter Berücksichtigung der Zeitzone keine Datums- / Uhrzeitangabe generiert ... Ich denke, es wird. .. .. In diesem Fall gehen wir individuell darauf ein.

Recommended Posts

[Python3] Eine Geschichte, die bei der Zeitzonenkonvertierung steckt
Eine Geschichte über den Umgang mit Binärdaten in Python
Eine Geschichte über einen Python-Anfänger, der mit dem No-Modul'http.server 'feststeckt.
Die Geschichte, mit Python eine Hanon-ähnliche Partitur zu machen
Probleme beim Erstellen eines CSV-JSON-Konvertierungstools mit Python
Eine Geschichte über das Ausprobieren eines (Golang +) Python-Monorepo mit Bazel
Stolpern Geschichte mit Python-Array
Machen Sie eine Lotterie mit Python
Messung der Ausführungszeit mit Python With
Erstellen Sie ein Verzeichnis mit Python
Ein bisschen im Kettenschiff stecken
Zeitsynchronisation (Windows) mit Python
Bestimmen Sie, ob die Zeichenfolge Zeit mit einem regulären Python-Ausdruck ist
Ich habe ein Paket erstellt, um Zeitreihen mit Python zu filtern
Eine Geschichte über einen Amateur, der mit Python (Kivy) einen Blockbruch macht ②
Eine Geschichte über einen Amateur, der mit Python (Kivy) einen Blockbruch macht ①
[Python] Was ist eine with-Anweisung?
Löse ABC163 A ~ C mit Python
Bedienen Sie den Belegdrucker mit Python
Python-Grafikhandbuch mit Matplotlib.
Lassen Sie uns eine GUI mit Python erstellen.
Löse ABC166 A ~ D mit Python
Erstellen Sie eine virtuelle Umgebung mit Python!
Ich habe mit Python eine Lotterie gemacht.
MP3 → WAV-Konvertierung mit Python
Erstellen einer virtuellen Umgebung mit Python 3
Löse ABC168 A ~ C mit Python
Erstellen Sie ein Empfehlungssystem mit Python
[Kleine Geschichte] Holen Sie sich mit Python einen Zeitstempel
[Python] Generiere ein Passwort mit Slackbot
Löse ABC162 A ~ C mit Python
Löse ABC167 A ~ C mit Python
Löse ABC158 A ~ C mit Python
Lassen Sie uns ein Diagramm mit Python erstellen! !!
[Python] Erbt eine Klasse mit Klassenvariablen
Ich habe mit Python einen Daemon erstellt
Schreiben Sie ein Batch-Skript mit Python3.5 ~
[Python, Selenium, PhantomJS] Eine Geschichte beim Scrapen einer Website mit fauler Last
Eine Geschichte über das Hinzufügen einer REST-API zu einem mit Python erstellten Daemon
Die Geschichte, einen Standardtreiber für db mit Python zu erstellen.
Eine Geschichte über die Entwicklung eines weichen Typs mit Firestore + Python + OpenAPI + Typescript
Die Geschichte, ein Modul zu erstellen, das E-Mails mit Python überspringt
[Pyenv] Erstellen einer Python-Umgebung mit Ubuntu 16.04
Spiralbuch in Python! Python mit einem Spiralbuch! (Kapitel 14 ~)
Eine Notiz, in der ein Python-Anfänger stecken blieb
Erstellen einer einfachen Power-Datei mit Python
[Python] Ein Programm, das Treppen mit # erstellt
Die Geschichte, wie man mit Python einen 100-Yen-Frühstücks-Bot für die Universität macht
Lassen Sie uns mit Python ein Shiritori-Spiel machen
Installieren Sie Python als Framework mit pyenv
Erstellen Sie mit Python + PIL ein Dummy-Image.
Ich habe mit Python einen Zeichenzähler erstellt
[Python] Zeichnen eines Wirbelmusters mit einer Schildkröte
Ich habe eine Heatmap mit Seaborn [Python] gezeichnet.
[Python] Erstellen Sie mit Anaconda eine virtuelle Umgebung
Erstellen wir mit Python eine kostenlose Gruppe