ऑफसेट-भोले और ऑफसेट-जागरूक डेटासेट को घटाया नहीं जा सकता


305

मेरे पास timestamptzPostgreSQL में एक समय क्षेत्र जागरूक क्षेत्र है। जब मैं तालिका से डेटा खींचता हूं, तो मैं अभी समय को घटाना चाहता हूं, इसलिए मैं इसकी आयु प्राप्त कर सकता हूं।

समस्या मैं आ रही है कि दोनों datetime.datetime.now()और datetime.datetime.utcnow()समय क्षेत्र अनजान timestamps है, जो मुझ में परिणाम यह त्रुटि मिलती लौटने के लिए लग रहे हैं:

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

क्या इससे बचने का कोई तरीका है (अधिमानतः बिना किसी थर्ड पार्टी मॉड्यूल का उपयोग किए)।

संपादित करें: सुझावों के लिए धन्यवाद, हालांकि टाइमजोन को समायोजित करने की कोशिश मुझे त्रुटियां देती प्रतीत होती है .. इसलिए मैं पीजी में टाइमजोन अनजान टाइमस्टैम्प का उपयोग करने जा रहा हूं और हमेशा उपयोग कर रहा हूं:

NOW() AT TIME ZONE 'UTC'

इस तरह मेरे सभी टाइमस्टैम्प डिफ़ॉल्ट रूप से UTC हैं (भले ही ऐसा करने के लिए अधिक कष्टप्रद हो)।

जवाबों:


316

क्या आपने टाइमजोन जागरूकता को हटाने की कोशिश की है?

से http://pytz.sourceforge.net/

naive = dt.replace(tzinfo=None)

समय क्षेत्र रूपांतरण भी जोड़ना पड़ सकता है।

संपादित करें: कृपया इस उत्तर की आयु से अवगत रहें। पायथन 3 का उत्तर नीचे है।


32
यह ऐसा करने का एकमात्र तरीका प्रतीत होता है। सुंदर लगता है कि अजगर को टाइमज़ोन के लिए इतना भद्दा समर्थन मिला है कि उसे टाइमस्टैम्प के साथ ठीक से काम करने के लिए एक तीसरे पक्ष के मॉड्यूल की आवश्यकता है ..
इयान

33
(सिर्फ रिकॉर्ड के लिए) वास्तव में समय क्षेत्र के बारे में जानकारी जोड़ना एक बेहतर विचार हो सकता है: stackoverflow.com/a/4530166/548696
Tadeck

7
भोली उम्र वाली वस्तुएं स्वाभाविक रूप से अस्पष्ट हैं और इसलिए उन्हें बचा जाना चाहिए। इसके बजाय tzinfo जोड़ना आसान है
jfs

1
@ किलोटन: यूटीसी इस संदर्भ में एक समयक्षेत्र है (जैसा कि tzinfo वर्ग द्वारा दर्शाया गया है)। को देखो datetime.timezone.utcया pytz.utc। उदाहरण के लिए, 1970-01-01 00:00:00अस्पष्ट है और आपको अवहेलना करने के लिए एक समय क्षेत्र जोड़ना होगा 1970-01-01 00:00:00 UTC:। आप देखते हैं, आपको नई जानकारी जोड़ना है; टाइमस्टैम्प अपने आप में अस्पष्ट है।
JFS

1
@JFSebastian: मुद्दा यह है कि utcnowबिना समयक्षेत्र के एक भोली वस्तु या एक टाइमस्टैम्प वापस नहीं आना चाहिए। डॉक्स से, "समय में एक विशिष्ट क्षण का प्रतिनिधित्व करने के लिए एक जागरूक वस्तु का उपयोग किया जाता है जो व्याख्या के लिए खुला नहीं है"। यूटीसी में कोई भी समय इस मानदंड को पूरा करता है, परिभाषा के अनुसार।
काइलोटन

213

सही समाधान समयक्षेत्र की जानकारी जोड़ना है, जैसे कि पायथन 3 में एक जागरूक डेटाइम ऑब्जेक्ट के रूप में वर्तमान समय प्राप्त करने के लिए:

from datetime import datetime, timezone

now = datetime.now(timezone.utc)

पुराने पायथन संस्करणों पर, आप 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)

10
TZ को हटाने से बेहतर है क्योंकि स्वीकृत उत्तर IMHO की वकालत करता है।
शौतिह

3
यहाँ अजगर
टाइमज़ोन

61

मुझे पता है कि कुछ लोग इस प्रकार के डेटाबेस इंटरैक्शन को अमूर्त करने के लिए विशेष रूप से Django का उपयोग करते हैं। Django उपयोगिताओं को प्रदान करता है जिनका उपयोग इसके लिए किया जा सकता है:

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

आपको एक बुनियादी Django सेटिंग इन्फ्रास्ट्रक्चर स्थापित करने की आवश्यकता है, भले ही आप इस प्रकार के इंटरफ़ेस का उपयोग कर रहे हों (सेटिंग्स में, आपको USE_TZ=Trueएक जागरूक डेटाइम प्राप्त करने के लिए शामिल करने की आवश्यकता है )।

अपने आप से, यह शायद कहीं नहीं है जो आपको इंटरफ़ेस के रूप में Django का उपयोग करने के लिए प्रेरित करने के लिए पर्याप्त है, लेकिन कई अन्य भत्ते भी हैं। दूसरी ओर, यदि आप यहां डगमगाते हैं क्योंकि आप अपने Django ऐप का उपयोग कर रहे थे (जैसा कि मैंने किया था), तो शायद यह मदद करता है ...


1
आपको USE_TZ=Trueयहां एक जागरूक डेटाइम प्राप्त करने की आवश्यकता है।
बजे

2
हां - मैं यह उल्लेख करना भूल गया कि आपको अपनी सेटिंग्स को सेट करने की आवश्यकता है क्योंकि जेएफसेबस्टियन का वर्णन है (मुझे लगता है कि यह 'सेट और भूलना' का एक उदाहरण था)।
ऋषि

और इसे आसानी से अन्य + timedelta(hours=5, minutes=30)
टाइमज़ोन में

25

यह एक बहुत ही सरल और स्पष्ट समाधान
है कोड की दो लाइनें

# 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

"गलत" आपके उत्तर में अंतिम वाक्य को संदर्भित करता है: "... जोड़ना चाहिए ... यूटीसी नहीं" - यूटीसी टाइमज़ोन यहां काम करता है और इसलिए बयान गलत है।
13

स्पष्ट होने का मेरा मतलब है: diff = datetime.now(timezone.utc) - your_timezone_aware_variableकाम करता है (और (a - b)ऊपर दिया गया सूत्र यह स्पष्टीकरण है कि (a - b)काम क्यों कर सकता है भले ही a.tzinfoवह क्यों न हो b.tzinfo)।
jfs

6

Psycopg2 मॉड्यूल की अपनी टाइमज़ोन परिभाषाएं हैं, इसलिए मैंने अपने स्वयं के आवरण को पूरी तरह से लिखना बंद कर दिया:

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

और pg_utcnowजब भी आपको PostgreSQL की तुलना करने के लिए वर्तमान समय की आवश्यकता हो, बस उपयोग करेंtimestamptz


कोई भी tzinfo ऑब्जेक्ट जो शून्य utc ऑफ़सेट लौटाता है, उदाहरण के लिए करेगा ।
jfs

6

मैंने भी उसी समस्या का सामना किया। फिर मुझे बहुत खोज के बाद एक उपाय मिला।

समस्या यह थी कि जब हम मॉडल या फॉर्म से डेटाइम ऑब्जेक्ट प्राप्त करते हैं, तो इसकी सूचना ऑफसेट होती है और अगर हमें सिस्टम द्वारा समय मिलता है तो यह ऑफसेट भोले है

तो मैंने जो किया वह है मुझे वर्तमान समय timezone.now () का उपयोग करके मिला और django.utils से टाइमज़ोन आयात करने के लिए टाइमज़ोन आयात करें और अपनी परियोजना सेटिंग फ़ाइल में USE_TZ = True डालें


2

मैं एक अति सरल समाधान के साथ आया:

import datetime

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

यह टाइमजोन-अवेयर और टाइमज़ोन-भोले डेटाइम दोनों मूल्यों के साथ काम करता है। और कोई अतिरिक्त पुस्तकालयों या डेटाबेस वर्कअराउंड की आवश्यकता नहीं है।


1

मैंने पाया timezone.make_aware(datetime.datetime.now())है कि django में सहायक है (मैं 1.9.1 पर हूं)। दुर्भाग्य से आप बस एक datetimeवस्तु को ऑफसेट-अवगत नहीं करा सकते हैं , फिर timetz()यह। आपको इसके datetimeआधार पर तुलना करनी होगी और तुलना करनी होगी ।


1

क्या वहाँ कुछ कारण है कि आप PostgreSQL में आयु गणना को क्यों नहीं संभाल सकते हैं? कुछ इस तरह

select *, age(timeStampField) as timeStampAge from myTable

2
हाँ वहाँ है .. लेकिन मैं ज्यादातर पूछ रहा था क्योंकि मैं पोस्टग्रे में सभी गणना करने से बचना चाहता हूं।
इयान

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

FYI करें, utc_to_local()मेरे उत्तर से स्थानीय समय एक जागरूक
डेटाइम

यह स्पष्ट नहीं है कि आपका कोड क्या करने की कोशिश कर रहा है। आप इसे बदल सकते हैं delta = response.tx_time - time.time()
9
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.