[Python3] Une histoire bloquée avec la conversion du fuseau horaire

J'écrivais un petit outil pour recevoir des informations, y compris l'heure d'une API externe et notifier Slack, mais je ne pouvais pas convertir le fuseau horaire de manière cohérente et stable, ce qui signifiait. Notez ce que vous avez appris au cours de l'enquête et les solutions possibles.

Résumé

--Utilisez pytz.timezone.localize (datetime) lorsque vous utilisez pytz pour la conversion du fuseau horaire. --Il semble préférable de ne pas utiliser le datetime.replace fourni avec datetime --Il existe deux types de datetime: Timezeon aware / Offest native --Si datetime est généré en Python sans en être conscient, le décalage natif est généré.

Complicité du traitement des fuseaux horaires en Python × pytz

Dans mon cas, il y avait plusieurs types de pierres d'achoppement.

  1. Timezeon aware / Offest native
  2. Rigueur Pytz
  3. spécifications pytz

Enfin, je vais vous montrer ce que j'ai recherché à ce sujet et quel type de code j'ai écrit pour les surmonter.

Timezeon aware / Offest native

Il existe les deux types de date / heure ci-dessus.

La prise en charge du fuseau horaire est une date / heure avec un fuseau horaire explicitement spécifié, et Offset native est une heure sans fuseau horaire spécifié. Les deux ne peuvent être comparés.

Il faut les distinguer.

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

#Les deux ne peuvent pas être comparés
d1 < d2
# >>> TypeError: can't compare offset-naive and offset-aware datetimes

La rigueur de pytz

Le fuseau horaire semble être déterminé par l'emplacement et ** l'heure **. Il semble y avoir un cas où la définition du décalage horaire est modifiée en raison de circonstances historiques, et notre Japon correspond également à cet exemple.

Il semble que le décalage horaire soit différent entre Tokyo jusqu'en 1888 et après cela, et avant 1888, il semble être ** + 09: 19 ** de UTC. Ainsi, pytz est strictement considéré, et il semble que cela soit pris en compte dans certaines méthodes du système de conversion de fuseau horaire (datetime.replace etc.).

import pytz

#Ici AFFICHER_TIMEZONE='Asia/Tokyo'Supposer
DISPLAY_TIMEZONE = os.environ.get('DISPLAY_TIMEZONE') 
tz = pytz.timezone(DISPLAY_TIMEZONE)

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

C'est 19 minutes de repos. Cependant, cela semble être correct en raison d'une conformité appropriée.

Si votre programme traite de ** Offset native **, vous pouvez utiliser timezone.localize () fourni par l'objet timezone de pytz. Il sera converti correctement à +09: 00.

** Tant que vous utilisez la méthode localize de pytz, la conversion du fuseau horaire se produira à +09: 00 comme vous le souhaitez, vous n'avez donc pas à vous soucier de cet écart. ** **

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

spécifications pytz

localize () de pytz n'accepte pas les fuseaux horaires compatibles avec le fuseau horaire. Ce point mérite attention.

# 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)

Par conséquent, quand il s'agit de ** l'heure consciente du fuseau horaire, localize ne peut pas être utilisé tel quel, et il est nécessaire de le convertir en décalage natif une fois. ** Cela se produit souvent lorsque l'heure renvoyée, comme l'utilisation d'un wrapper pour une API externe, prend en compte le fuseau horaire.

Le contenu écrit ici mentionne les spécifications de pytz ont changé | Qiita @ higitune, vous devriez donc y jeter un coup d'œil. .. Ce sera très utile, y compris la section des commentaires.

Convertir un type de datetime tenant compte du fuseau horaire en un autre fuseau horaire

Lors de l'utilisation d'un package développé par quelqu'un d'autre que vous-même, il appartient à l'implémenteur de savoir si l'heure renvoyée par le module de package est compatible avec le fuseau horaire ou offset native. Afin de les combiner et de mettre en œuvre vos propres exigences de développement, vous devrez gérer un mélange de ces derniers.

Par conséquent, il est préférable de le déposer une fois sur l'horodatage (bien qu'il soit sur Theory Street). Le code ci-dessous vous permet de convertir JST à la fois en fonction du décalage natif / du fuseau horaire. (Il n'est pas confirmé si des résultats similaires peuvent être obtenus dans d'autres pays)

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

Résumé

Le fuseau horaire est déroutant ...

Le code ci-dessus est publié comme une implémentation de référence, mais il semble que l'heure localisée réelle soit principalement requise pour la couche de présentation (c'est-à-dire l'apparence), donc dans la couche logique et la couche de persistance autant que possible avec datetime Je pense qu'il serait préférable d'utiliser plutôt l'horodatage.

Cependant, datetime est extrêmement pratique lors de l'écriture de code, et je pense qu'il existe de nombreux cas où les opérations de date doivent être effectuées fréquemment dans la couche logique.

Dans un tel cas, je pense qu'il vaut mieux unifier lequel des fuseaux horaires awre / offset natif est utilisé, au moins dans le code plus proche de la couche logique écrite par moi-même. Si les heures gérées par les packages externes ne sont pas cohérentes, vous pouvez créer votre propre calque qui enveloppe finement l'interface que vous souhaitez utiliser et absorbe l'incohérence dans le type de date / heure et le fuseau horaire. , Je pense que la logique principale sera plus propre.

Je ne peux pas décider lequel envoyer en fonction de mes connaissances actuelles, alors je vous serais reconnaissant si vous pouviez nous donner votre avis dans la section commentaires.

Il existe également des pièges subtils dans le code ci-dessus. Par exemple

"Le type datetime renvoyé par la fonction / méthode est un offset natif, mais l'heure est basée sur UTC."

Dans ce cas. Si l'API du service externe est conçue pour renvoyer de manière cohérente UTC et que l'implémentation du package externe qui l'encapsule n'est pas cool et ne génère pas de datetime compte tenu du fuseau horaire ... Je pense que ce sera. .. .. Dans ce cas, traitons-le individuellement.

Recommended Posts

[Python3] Une histoire bloquée avec la conversion du fuseau horaire
Une histoire sur la gestion des données binaires en Python
Une histoire à propos d'un débutant en python coincé avec aucun module nommé'ttp.server '
L'histoire de la création d'une partition de type Hanon avec Python
Problèmes lors de la création d'un outil de conversion csv-json avec python
Une histoire d'essayer un monorepo (Golang +) Python avec Bazel
Histoire de trébucher avec le tableau Python
Faites une loterie avec Python
Mesure du temps d'exécution avec Python avec
Créer un répertoire avec python
Un peu coincé dans le chainer
Synchronisation de l'heure (Windows) avec Python
Déterminer si la chaîne est l'heure avec une expression régulière python
J'ai créé un package pour filtrer les séries chronologiques avec python
Une histoire sur un amateur faisant une rupture de bloc avec python (kivy) ②
Une histoire sur un amateur faisant une rupture de bloc avec python (kivy) ①
[Python] Qu'est-ce qu'une instruction with?
Résoudre ABC163 A ~ C avec Python
Faites fonctionner l'imprimante de reçus avec python
Manuel de graphisme Python avec Matplotlib.
Faisons une interface graphique avec python.
Résoudre ABC166 A ~ D avec Python
Créez un environnement virtuel avec Python!
J'ai fait une loterie avec Python.
Conversion MP3 → WAV avec Python
Créer un environnement virtuel avec Python 3
Résoudre ABC168 A ~ C avec Python
Créer un système de recommandation avec python
[Petite histoire] Obtenez l'horodatage avec Python
[Python] Générer un mot de passe avec Slackbot
Résoudre ABC162 A ~ C avec Python
Résoudre ABC167 A ~ C avec Python
Résoudre ABC158 A ~ C avec Python
Faisons un graphe avec python! !!
[Python] Hériter d'une classe avec des variables de classe
J'ai créé un démon avec Python
Ecrire un script batch avec Python3.5 ~
[Python, Selenium, PhantomJS] Une histoire lors de la capture d'un site Web avec une charge paresseuse
Une histoire sur l'ajout d'une API REST à un démon créé avec Python
L'histoire de la création d'un pilote standard pour db avec python.
Une histoire sur le développement d'un type logiciel avec Firestore + Python + OpenAPI + Typescript
L'histoire de la création d'un module qui ignore le courrier avec python
[Pyenv] Construire un environnement python avec ubuntu 16.04
Livre en spirale en Python! Python avec un livre en spirale! (Chapitre 14 ~)
Une note où un débutant Python s'est retrouvé coincé
Créer un fichier power simple avec Python
[Python] Un programme qui crée des escaliers avec #
L'histoire de la création d'un robot LINE pour le petit-déjeuner d'une université de 100 yens avec Python
Faisons un jeu de shiritori avec Python
Installer Python en tant que Framework avec pyenv
Créez une image factice avec Python + PIL.
J'ai fait un compteur de caractères avec Python
[Python] Dessiner un motif de tourbillon avec une tortue
J'ai dessiné une carte thermique avec Seaborn [Python]
[Python] Créez un environnement virtuel avec Anaconda
Créons un groupe gratuit avec Python