web-dev-qa-db-fra.com

Impossible de soustraire des dates / heures naïves et conscientes de l'offset

J'ai un champ timestamptz sensible au fuseau horaire dans PostgreSQL. Lorsque je tire des données de la table, je veux ensuite soustraire le temps présent afin de pouvoir obtenir son âge.

Le problème que je rencontre est que datetime.datetime.now() et datetime.datetime.utcnow() semblent tous deux renvoyer des horodatages sans fuseau horaire, ce qui me donne l'erreur suivante:

TypeError: can't subtract offset-naive and offset-aware datetimes 

Y a-t-il un moyen d'éviter cela (de préférence sans utiliser un module tiers).

EDIT: Merci pour les suggestions, mais essayer d’ajuster le fuseau horaire semble me donner des erreurs .. donc je vais juste utiliser un timezamp de timezone non conscient dans PG et toujours insérer en utilisant:

NOW() AT TIME ZONE 'UTC'

Ainsi, tous mes horodatages sont au format UTC par défaut (même s’il est plus ennuyant de le faire).

249
Ian

avez-vous essayé de supprimer la conscience du fuseau horaire?

de http://pytz.sourceforge.net/

naive = dt.replace(tzinfo=None)

vous devrez peut-être ajouter une conversion de fuseau horaire.

edit: S'il vous plaît être conscient de l'âge de cette réponse. Une réponse Python 3 est présentée ci-dessous.

267
phillc

La solution correcte consiste à ajouter les informations de fuseau horaire, par exemple, pour obtenir l'heure actuelle en tant qu'objet date/heure conscient dans Python 3:

from datetime import datetime, timezone

now = datetime.now(timezone.utc)

Sur les anciennes versions Python, vous pouvez définir vous-même l'objet utc tzinfo (exemple, à partir de docs datetime):

from datetime import tzinfo, timedelta, datetime

ZERO = timedelta(0)

class UTC(tzinfo):
  def utcoffset(self, dt):
    return ZERO
  def tzname(self, dt):
    return "UTC"
  def dst(self, dt):
    return ZERO

utc = UTC()

ensuite:

now = datetime.now(utc)
151
jfs

Je sais que certaines personnes utilisent Django spécifiquement comme interface permettant de résumer ce type d'interaction avec une base de données. Django fournit des utilitaires pouvant être utilisés à cette fin:

from Django.utils import timezone
now_aware = timezone.now()

Vous devez configurer une infrastructure de base Django paramètres, même si vous utilisez simplement ce type d'interface (dans les paramètres, vous devez inclure USE_TZ=True pour obtenir une date/heure consciente).

En soi, cela n’est probablement pas suffisant pour vous motiver à utiliser Django comme interface, mais il existe de nombreux autres avantages. D'un autre côté, si vous avez trébuché ici parce que vous manipuliez votre application Django (comme je l'ai fait), alors peut-être que cela vous aidera ...

54
sage

C'est une solution très simple et claire
Deux lignes de code

# First we obtain de timezone info o some datatime variable    

tz_info = your_timezone_aware_variable.tzinfo

# Now we can subtract two variables using the same time zone info
# For instance
# Lets obtain the Now() datetime but for the tz_info we got before

diff = datetime.datetime.now(tz_info)-your_timezone_aware_variable

Conclusion: Vous devez gérer vos variables datetime avec les mêmes informations temporelles

17
ePi272314

Le module psycopg2 a ses propres définitions de fuseau horaire, alors j'ai fini par écrire mon propre wrapper autour de utcnow:

def pg_utcnow():
    import psycopg2
    return datetime.utcnow().replace(
        tzinfo=psycopg2.tz.FixedOffsetTimezone(offset=0, name=None))

et utilisez simplement pg_utcnow à chaque fois que vous avez besoin de l'heure actuelle pour la comparer à un PostgreSQL timestamptz

5
erjiang

J'ai aussi fait face au même problème. Ensuite, j'ai trouvé une solution après de nombreuses recherches.

Le problème était que lorsque nous obtenons l'objet datetime du modèle ou de la forme, il est connaissance de l'offset et si nous obtenons l'heure par système, il l'est offset naïf.

Donc ce que j’ai fait, c’est que j’ai eu l’heure actuelle en utilisant timezone.now () et en important le fuseau horaire par depuis Django.utils import timezone et en plaçant le SE_TZ = True dans le fichier de paramètres de votre projet.

4
Ashok Joshi

Je suis venu avec une solution ultra-simple:

import datetime

def calcEpochSec(dt):
    epochZero = datetime.datetime(1970,1,1,tzinfo = dt.tzinfo)
    return (dt - epochZero).total_seconds()

Il fonctionne avec des valeurs de date/heure naïves à chaque fuseau horaire. Et aucune autre bibliothèque ou solution de rechange à la base de données n'est requise.

2
John L. Stanley

Existe-t-il une raison pressante de ne pas pouvoir gérer le calcul de l'âge dans PostgreSQL lui-même? Quelque chose comme

select *, age(timeStampField) as timeStampAge from myTable
1
Nic Gibson

J'ai trouvé que timezone.make_aware(datetime.datetime.now()) est utile dans Django (je suis sur 1.9.1). Malheureusement, vous ne pouvez pas simplement créer un objet datetime avec décalage, puis timetz(). Vous devez faire un datetime et faire des comparaisons sur cette base.

1
Joseph Coco

Je sais que c'est vieux, mais je pensais ajouter ma solution au cas où quelqu'un le jugerait utile.

Je voulais comparer le date-heure naïf local avec une date-heure consciente d'un serveur de temps. En gros, j'ai créé un nouvel objet date-heure naïf à l'aide de l'objet conscient de date-heure. C'est un peu un bidouillage et ne semble pas très jolie, mais fait le travail.

import ntplib
import datetime
from datetime import timezone

def utc_to_local(utc_dt):
    return utc_dt.replace(tzinfo=timezone.utc).astimezone(tz=None)    

try:
    ntpt = ntplib.NTPClient()
    response = ntpt.request('pool.ntp.org')
    date = utc_to_local(datetime.datetime.utcfromtimestamp(response.tx_time))
    sysdate = datetime.datetime.now()

... voici le fudge ...

    temp_date = datetime.datetime(int(str(date)[:4]),int(str(date)[5:7]),int(str(date)[8:10]),int(str(date)[11:13]),int(str(date)[14:16]),int(str(date)[17:19]))
    dt_delta = temp_date-sysdate
except Exception:
    print('Something went wrong :-(')
0
I_do_python