Невозможно вычесть смещенные наивные и смещенные даты - PullRequest
234 голосов
/ 28 апреля 2009

У меня есть поле timestamptz, осведомленное о часовом поясе в PostgreSQL. Когда я извлекаю данные из таблицы, я хочу вычесть время прямо сейчас, чтобы узнать его возраст.

Проблема, с которой я столкнулся, заключается в том, что и datetime.datetime.now(), и datetime.datetime.utcnow(), похоже, возвращают временные метки, не зависящие от часового пояса, что приводит к получению этой ошибки:

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

Есть ли способ избежать этого (желательно без использования стороннего модуля).

РЕДАКТИРОВАТЬ: Спасибо за предложения, однако попытка отрегулировать часовой пояс, кажется, дает мне ошибки .. поэтому я просто собираюсь использовать временные метки, не зависящие от часового пояса, в PG и всегда вставлять, используя:

NOW() AT TIME ZONE 'UTC'

Таким образом, все мои временные метки по умолчанию установлены в формате UTC (хотя это более раздражает).

Ответы [ 10 ]

255 голосов
/ 28 апреля 2009

Вы пытались удалить информацию о часовом поясе?

от http://pytz.sourceforge.net/

naive = dt.replace(tzinfo=None)

может также потребоваться добавить преобразование часового пояса.

edit: Пожалуйста, имейте в виду возраст этого ответа. Python 3 ответ ниже.

142 голосов
/ 04 сентября 2014

Правильным решением является добавление информации о часовом поясе, например, чтобы получить текущее время как осведомленный объект даты и времени в Python 3:

from datetime import datetime, timezone

now = datetime.now(timezone.utc)

В более старых версиях Python вы можете определить объект utc tzinfo самостоятельно (пример из документа 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()

, то:

now = datetime.now(utc)
49 голосов
/ 22 июня 2015

Я знаю, что некоторые люди используют Django специально как интерфейс для абстрагирования этого типа взаимодействия с базой данных. Django предоставляет утилиты, которые можно использовать для этого:

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

Вам необходимо настроить базовую инфраструктуру настроек Django, даже если вы просто используете этот тип интерфейса (в настройках вам нужно включить USE_TZ=True, чтобы узнать дату и время).

Само по себе это, вероятно, далеко не достаточно, чтобы мотивировать вас использовать Django в качестве интерфейса, но есть много других преимуществ. С другой стороны, если вы споткнулись здесь, потому что искали приложение Django (как я), то, возможно, это поможет ...

14 голосов
/ 03 октября 2015

Это очень простое и понятное решение
Две строки кода

# 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

Вы должны управлять переменными даты и времени с одинаковой информацией времени

5 голосов
/ 19 июля 2013

Модуль psycopg2 имеет свои собственные определения часовых поясов, поэтому я написал свою обертку вокруг utcnow:

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

и просто используйте pg_utcnow всякий раз, когда вам нужно текущее время для сравнения с PostgreSQL timestamptz

4 голосов
/ 22 февраля 2016

Я тоже сталкивался с такой же проблемой. После долгих поисков я нашел решение.

Проблема заключалась в том, что когда мы получаем объект datetime из модели или формы, он учитывает смещение , а если мы получаем время по системе, он смещен наивно .

Итак, я установил текущее время с помощью timezone.now () и импортировал часовой пояс на из django.utils import timezone и поместил USE_TZ = True в файле настроек вашего проекта.

2 голосов
/ 15 мая 2018

Я придумал сверхпростое решение:

import datetime

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

Работает как со значениями даты и времени, так и с учетом часовых поясов. И никаких дополнительных библиотек или обходных путей к базе данных не требуется.

1 голос
/ 25 июля 2016

Я нашел timezone.make_aware(datetime.datetime.now()) полезен в django (я на 1.9.1). К сожалению, вы не можете просто сделать объект datetime со смещением, тогда timetz() это. Вы должны сделать datetime и сделать сравнения на основе этого.

1 голос
/ 28 апреля 2009

Вы можете рассчитывать возраст в самом PostgreSQL. Что-то вроде

select *, age(timeStampField) as timeStampAge from myTable
0 голосов
/ 29 апреля 2014

Я знаю, что это старое, но просто подумал, что добавлю свое решение на тот случай, если кто-то посчитает его полезным.

Я хотел сравнить местное наивное время-дату с осведомленным временем-датой с сервера времени. Я в основном создал новый наивный объект datetime, используя осведомленный объект datetime. Это что-то вроде хака и выглядит не очень красиво, но выполняет свою работу.

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

... вот и выдумка ...

    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 :-(')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...