オフセットナイーブおよびオフセット対応の日時を減算できません


305

timestamptzPostgreSQLにタイムゾーン対応フィールドがあります。テーブルからデータをプルするときは、時間を差し引いて、経過時間を取得します。

私が抱えている問題は、両方でdatetime.datetime.now()あり、datetime.datetime.utcnow()タイムゾーンを認識しないタイムスタンプを返すようです。その結果、次のエラーが発生します。

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

これを回避する方法はありますか(サードパーティのモジュールを使用しないことが望ましい)。

編集:提案をありがとう、ただしタイムゾーンを調整しようとするとエラーが発生するようです..それで、PGでタイムゾーンを認識しないタイムスタンプを使用し、常に以下を使用して挿入します:

NOW() AT TIME ZONE 'UTC'

そうすれば、私のタイムスタンプはすべてデフォルトでUTCになります(これを行うのはもっと面倒ですが)。

回答:


316

タイムゾーン認識を削除しようとしましたか?

http://pytz.sourceforge.net/から

naive = dt.replace(tzinfo=None)

タイムゾーン変換も追加する必要がある場合があります。

編集:この回答の年齢に注意してください。Python 3の答えは以下のとおりです。


32
これが唯一の方法のようです。Pythonの年代は、それが適切にタイムスタンプを持つ作品にサードパーティ製のモジュールを必要としていることをタイムゾーンのためのそのような安っぽいサポートを得たことはかなりラメのようだ
イアン

33
(参考までに)タイムゾーンに関する情報を実際に追加することをお勧めします:stackoverflow.com/a/4530166/548696
Tadeck

7
ナイーブな日時オブジェクトは本質的にあいまいであるため、避ける必要があります。代わりにtzinfoを追加するの
jfs

1
@Kylotan:UTCはこのコンテキストのタイムゾーンです(tzinfoクラスで表されます)。datetime.timezone.utcまたはを見てくださいpytz.utc。たとえば、1970-01-01 00:00:00はあいまいであり、明確にするためにタイムゾーンを追加する必要があります1970-01-01 00:00:00 UTC。ご覧のとおり、新しい情報を追加する必要があります。タイムスタンプ自体はあいまいです。
jfs 2015

1
@JFSebastian:問題はutcnow、タイムゾーンのない単純なオブジェクトまたはタイムスタンプを返すべきではないということです。ドキュメントから、「認識可能なオブジェクトは、解釈が許されない特定の瞬間を表すために使用されます」。定義により、UTCではいつでもこの基準を満たします。
Kylotan 2015

214

正しい解決策は、タイムゾーン情報を追加することです。たとえば、現在の時刻をPython 3の認識された日時オブジェクトとして取得します。

from datetime import datetime, timezone

now = datetime.now(timezone.utc)

古いバージョンのPythonでは、utctzinfoオブジェクトを自分で定義できます(datetime docsの例):

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)

10
受け入れられた回答が私見を支持しているので、tzを削除するよりはましです。
Shautieh 2017年

3
ここでは、Pythonのタイムゾーンのリストは、次のとおりです。stackoverflow.com/questions/13866926/...
comfytoday

61

この種のデータベース相互作用を抽象化するためのインターフェースとしてDjangoを使用している人がいることは知っています。Djangoはこれに使用できるユーティリティを提供します:

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

このタイプのインターフェースを使用しているだけの場合でも、基本的なDjango設定インフラストラクチャを設定する必要があります(設定では、USE_TZ=True日時を取得するために含める必要があります)。

これだけではおそらく、Djangoをインターフェースとして使用する動機を与えるのに十分近くはありませんが、他にも多くの特典があります。一方、Djangoアプリを(私がしたように)マングルしているためにここでつまずいた場合は、おそらくこれが役立ちます...


1
あなたが必要とするUSE_TZ=True、ここで注意してください日時を取得するには、。
jfs

2
はい-JFSebastianが説明するように、settings.pyを設定する必要があることを忘れていました(これは「設定して忘れる」のインスタンスだったと思います)。
セージ

また+ timedelta(hours=5, minutes=30)、IST
ABcDexter

25

これは非常にシンプルで明確なソリューションです
2行のコード

# 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

結論:同じ時刻情報で日時変数を管理する必要があります


違いますか?私が書いたコードはテスト済みで、djangoプロジェクトで使用しています。それははるかに明確かつ簡単です
ePi272314

「不正解」とは、回答の最後の文を指します。「... UTCではなく...を追加する必要があります」 -UTCタイムゾーンはここで機能するため、ステートメントが正しくありません。
jfs

明確にするために、私は意味します:diff = datetime.now(timezone.utc) - your_timezone_aware_variable動作します(そして(a - b)上記の式は、(a - b)動作しなくても動作する理由a.tzinfoですb.tzinfo)。
jfs

6

psycopg2モジュールには独自のタイムゾーン定義があるため、utcnowの周りに独自のラッパーを作成してしまいました。

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

pg_utcnowPostgreSQLと比較するために現在の時刻が必要なときはいつでも使用してくださいtimestamptz


たとえば、ゼロのutcオフセットを返すtzinfoオブジェクトが実行します
jfs

6

私も同じ問題に直面しました。それから私は多くの検索の後に解決策を見つけました。

問題は、モデルまたはフォームからdatetimeオブジェクトを取得すると、オフセットに対応しており、システムで時刻を取得すると、ナイーブにオフセットされることです。

だから私が何をしたか、私が使用して現在の時刻得ですtimezone.nowを()とでタイムゾーンをインポートdjango.utilsインポートタイムゾーンからと入れUSE_TZ =真のプロジェクトの設定ファイルに。


2

私は超シンプルなソリューションを思いつきました:

import datetime

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

これは、timezone-awareとtimezone-naiveの両方の日時値で機能します。また、追加のライブラリやデータベースの回避策は必要ありません。


1

私はtimezone.make_aware(datetime.datetime.now())djangoで役に立ちます(私は1.9.1を使用しています)。残念ながら、単純にdatetimeオブジェクトをオフセット対応にすることはできませんtimetz()。aを作成し、datetimeそれに基づいて比較する必要があります。


1

PostgreSQL自体で年齢計算を処理できない差し迫った理由はありますか?何かのようなもの

select *, age(timeStampField) as timeStampAge from myTable

2
はい、あります。しかし、postgreですべての計算を実行したくないので、私はほとんど尋ねていました。
イアン、

0

私はこれが古いことを知っていますが、誰かが便利だと思った場合に備えて、私のソリューションを追加すると思いました。

ローカルのナイーブな日時とタイムサーバーからの認識された日時を比較したいと思いました。基本的には、認識された日時オブジェクトを使用して、新しい単純な日時オブジェクトを作成しました。これは少しハックであり、見た目はきれいではありませんが、仕事は完了です。

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

参考utc_to_local()までに、私の回答から、ローカル時間を認識された日時オブジェクトとして返します(Python 3.3以降のコードです)
jfs

コードが何をしようとしているのかは明確ではありません。に置き換えることができdelta = response.tx_time - time.time()ます。
jfs
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.