簡體   English   中英

不能減去偏移天真和偏移感知日期時間

[英]Can't subtract offset-naive and offset-aware datetimes

我在 PostgreSQL 中有一個時區感知的timestamptz字段。 當我從表中提取數據時,我想減去現在的時間,這樣我就可以得到它的年齡。

我遇到的問題是datetime.datetime.now()datetime.datetime.utcnow()似乎都返回時區不知道的時間戳,這導致我收到此錯誤:

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

有沒有辦法避免這種情況(最好不使用第三方模塊)。

編輯:感謝您的建議,但是嘗試調整時區似乎給了我錯誤..所以我將在 PG 中使用時區不知道的時間戳,並始終使用以下方法插入:

NOW() AT TIME ZONE 'UTC'

這樣我所有的時間戳默認都是UTC(即使這樣做更煩人)。

您是否嘗試刪除時區意識?

來自http://pytz.sourceforge.net/

naive = dt.replace(tzinfo=None)

可能還需要添加時區轉換。

編輯:請注意這個答案的年齡。 下面是一個涉及添加時區信息而不是在 python 3 中刪除它的答案。 https://stackoverflow.com/a/25662061/93380

正確的解決方案是添加時區信息,例如,在 Python 3 中獲取當前時間作為感知日期時間對象:

from datetime import datetime, timezone

now = datetime.now(timezone.utc)

在較舊的 Python 版本上,您可以自己定義utc tzinfo 對象(來自日期時間文檔的示例):

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)

我知道有些人專門使用 Django 作為接口來抽象這種類型的數據庫交互。 Django 提供了可用於此目的的實用程序:

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

您確實需要設置一個基本的 Django 設置基礎結構,即使您只是使用這種類型的界面(在設置中,您需要包含USE_TZ=True以獲取日期時間)。

就其本身而言,這可能遠遠不足以激勵您使用 Django 作為界面,但還有許多其他好處。 另一方面,如果您因為正在破壞 Django 應用程序(就像我所做的那樣)而偶然發現這里,那么這可能會有所幫助...

這是一個非常簡單明了的解決方案
兩行代碼

# 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

結論:您必須使用相同的時間信息管理日期時間變量

我也面臨同樣的問題。 然后我經過大量搜索找到了解決方案。

問題是,當我們從模型或表單中獲取 datetime 對象時,它是偏移感知的,如果我們通過系統獲取時間,它就是偏移 naive

所以我所做的是使用timezone.now()獲取當前時間並通過from django.utils import timezone 導入時區並將USE_TZ = True放在您的項目設置文件中。

你不需要標准庫之外的任何東西

datetime.datetime.now().astimezone()

如果您只是更換時區,則不會調整時間。 如果您的系統已經是 UTC,那么 .replace(tz='UTC') 就可以了。

>>> x=datetime.datetime.now()
datetime.datetime(2020, 11, 16, 7, 57, 5, 364576)

>>> print(x)
2020-11-16 07:57:05.364576

>>> print(x.astimezone()) 
2020-11-16 07:57:05.364576-07:00

>>> print(x.replace(tzinfo=datetime.timezone.utc)) # wrong
2020-11-16 07:57:05.364576+00:00

psycopg2 模塊有自己的時區定義,所以我最終圍繞 utcnow 編寫了自己的包裝器:

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

只要您需要當前時間與 PostgreSQL timestamptz進行比較,就使用pg_utcnow

我想出了一個超簡單的解決方案:

import datetime

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

它適用於時區感知和時區天真日期時間值。 並且不需要額外的庫或數據庫解決方法。

我發現timezone.make_aware(datetime.datetime.now())在 Django 中很有幫助(我在 1.9.1)。 不幸的是,您不能簡單地使datetime對象偏移感知,然后timetz()它。 您必須制作一個datetime並基於此進行比較。

標題中問題的最直接的答案似乎缺失了:

naive_date.replace(tzinfo=aware_date.tzinfo) - aware_date

是否有一些緊迫的原因導致您無法處理 PostgreSQL 本身的年齡計算? 就像是

select *, age(timeStampField) as timeStampAge from myTable

我知道這是舊的,但只是想我會添加我的解決方案,以防有人發現它有用。

我想將本地朴素的日期時間與來自時間服務器的已知日期時間進行比較。 我基本上使用感知日期時間對象創建了一個新的原始日期時間對象。 這有點像黑客,看起來不是很漂亮,但可以完成工作。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM