रेल जोन और पोस्टग्रेक्यूएल में समय क्षेत्र की पूरी तरह से अनदेखी करना


164

मैं रेल और पोस्टग्रेज में तारीखों और समय के साथ काम कर रहा हूं और इस मुद्दे पर चल रहा हूं:

डेटाबेस UTC में है।

उपयोगकर्ता रेल एप्लिकेशन में पसंद का टाइम-ज़ोन सेट करता है, लेकिन इसका उपयोग केवल उपयोगकर्ताओं द्वारा स्थानीय समय की तुलना करने के लिए किया जाता है।

उपयोगकर्ता 17 मार्च, 2012, शाम 7 बजे कहते हैं, एक बार स्टोर करता है। मैं टाइमजोन रूपांतरण या समयक्षेत्र संग्रहीत नहीं करना चाहता। मैं सिर्फ वह तारीख और समय बचाना चाहता हूं। इस तरह अगर उपयोगकर्ता ने अपना समय क्षेत्र बदल दिया, तो यह अभी भी 17 मार्च 2012, शाम 7 बजे दिखाएगा।

मैं केवल उपयोगकर्ताओं को स्थानीय समय क्षेत्र में वर्तमान समय के बाद 'पहले' या 'रिकॉर्ड' प्राप्त करने के लिए निर्दिष्ट समय क्षेत्र का उपयोग करता हूं।

मैं वर्तमान में 'टाइम ज़ोन के बिना टाइमस्टैम्प' का उपयोग कर रहा हूं, लेकिन जब मैं रिकॉर्ड्स को पुनः प्राप्त करता हूं, तो रेल (?) उन्हें ऐप में टाइम ज़ोन में परिवर्तित कर देता है, जो मुझे नहीं चाहिए।

Appointment.first.time
 => Fri, 02 Mar 2012 19:00:00 UTC +00:00 

क्योंकि डेटाबेस में रिकॉर्ड UTC के रूप में सामने आते हैं, मेरा हैक वर्तमान समय लेने के लिए है, 'Date.strptime (str, "% m /% d /% Y") के साथ समय क्षेत्र को हटा दें और फिर मेरा करें उस के साथ क्वेरी करें:

.where("time >= ?", date_start)

ऐसा लगता है कि सभी समय क्षेत्रों को अनदेखा करने का एक आसान तरीका होना चाहिए। कोई विचार?

जवाबों:


347

डेटा प्रकार timestampके लिए संक्षिप्त नाम है timestamp without time zone
अन्य विकल्प के timestamptzलिए कम है timestamp with time zone

timestamptzहै पसंदीदा दिनांक / समय परिवार में प्रकार, सचमुच। इसमें typispreferredसेट किया गया है pg_type, जो प्रासंगिक हो सकता है:

आंतरिक भंडारण और युग

आंतरिक रूप से, टाइमस्टैम्प डिस्क और रैम में 8 बाइट्स के भंडारण पर कब्जा कर लेता है । यह एक पूर्णांक मान है, जो पोस्टग्रेज युग, 2000-01-01 00:00:00 यूटीसी से माइक्रोसेकंड की गिनती का प्रतिनिधित्व करता है।

पोस्टग्रिज में आमतौर पर यूनिक्स की गणना के लिए आमतौर पर उपयोग किए जाने वाले यूनिक्स समय गिनती के सेकंड, 1970-01-01 00:00:00 यूटीसी से भी ज्ञान होता है , और यह फ़ंक्शन to_timestamp(double precision)या में उपयोग करता है EXTRACT(EPOCH FROM timestamptz)

स्रोत कोड:

* टाइमस्टैम्प, साथ ही अंतराल के एच / एम / एस फ़ील्ड, के रूप में संग्रहीत किए जाते हैं
* microseconds की इकाइयों के साथ int64 मान। (एक बार जब वे थे  
* सेकंड की इकाइयों के साथ डबल मान।)

तथा:

/ * यूनिक्स में दिन 0 के जूलियन-डेट समकक्ष और पोस्टग्रॉउंडिंग * /  
#define UNIX_EPOCH_JDATE 2440588 / * == date2j (1970, 1, 1, *) / /  
#define POSTGRES_EPOCH_JDATE 2451545 / * == date2j (2000, 1, 1) * /  

माइक्रोसेकंड रिज़ॉल्यूशन सेकंड के लिए अधिकतम 6 भिन्न अंकों का अनुवाद करता है।

timestamp

पोस्टग्रेज बताता है कि कोई मान टाइप किया गया है कि कोई समय क्षेत्र स्पष्ट रूप से प्रदान नहीं किया गया है। वर्तमान समय क्षेत्र मान लिया गया है। पोस्टग्रेट्स गलती से जोड़े गए किसी भी समय क्षेत्र संशोधक की उपेक्षा करता है!timestamp [without time zone]

प्रदर्शन के लिए कोई भी घंटे स्थानांतरित नहीं किए जाते हैं। एक ही समय क्षेत्र के साथ सब ठीक है। एक अलग समय क्षेत्र के लिए अर्थ परिवर्तन सेट करता है, लेकिन मूल्य और प्रदर्शन समान रहते हैं।

timestamptz

हैंडलिंग timestamp with time zoneअलग-अलग है। मैं मैनुअल यहाँ उद्धृत करता हूँ :

के लिए timestamp with time zone, आंतरिक रूप से संग्रहीत मूल्य हमेशा UTC में होता है (यूनिवर्सल समन्वित समय ...)

बोल्ड जोर मेरा। समय क्षेत्र में ही जमा हो जाती है कभी नहीं है । यह एक इनपुट संशोधक है जिसका उपयोग UTC टाइमस्टैम्प के अनुसार गणना करने के लिए किया जाता है, जिसे संग्रहीत किया जाता है - या आउटपुट मॉडिफायर का उपयोग स्थानीय समय को प्रदर्शित करने के लिए किया जाता है - जो कि समय क्षेत्र की भरपाई के साथ होता है। यदि आप timestamptzइनपुट पर ऑफसेट नहीं करते हैं , तो सत्र का वर्तमान समय क्षेत्र सेटिंग मान लिया गया है। सभी गणना यूटीसी टाइमस्टैम्प मूल्यों के साथ की जाती हैं। यदि आपको एक से अधिक समय क्षेत्र के साथ (या हो सकता है) सौदा करना है, तो उपयोग करें timestamptz

Psql या pgAdmin जैसे ग्राहक या libpq के माध्यम से संचार करने वाले किसी भी एप्लिकेशन (जैसे कि pg रत्न के साथ Ruby) को टाइमस्टैम्प प्लस ऑफसेट के साथ वर्तमान समय क्षेत्र के लिए या एक अनुरोध किए गए समय क्षेत्र (नीचे देखें) के अनुसार प्रस्तुत किया जाता है । यह हमेशा समय में एक ही बिंदु होता है , केवल प्रदर्शन प्रारूप बदलता रहता है। या, जैसा कि मैनुअल कहता है :

UTC में सभी टाइमज़ोन-जागरूक तिथियों और समय को आंतरिक रूप से संग्रहीत किया जाता है। क्लाइंट को प्रदर्शित किए जाने से पहले वे टाइमज़ोन कॉन्फ़िगरेशन पैरामीटर द्वारा निर्दिष्ट क्षेत्र में स्थानीय समय में परिवर्तित हो जाते हैं ।

इस सरल उदाहरण पर विचार करें (psql में):

db = # Select timestamptz '2012-03-05 20:00 +03 ';
      timestamptz
------------------------
 2012-03-05 18:00:00 +01

बोल्ड जोर मेरा। यहाँ क्या हुआ?
मैंने +3इनपुट शाब्दिक के लिए एक मनमाना समय क्षेत्र ऑफसेट चुना । पोस्टग्रेज करने के लिए, यह यूटीसी टाइमस्टैम्प इनपुट करने के कई तरीकों में से एक है 2012-03-05 17:00:00। क्वेरी का परिणाम मेरे परीक्षण में वियना / ऑस्ट्रिया की स्थापना के वर्तमान समय क्षेत्र के लिए प्रदर्शित होता है , जिसमें सर्दियों के दौरान और गर्मियों के समय में एक ऑफसेट होता है : क्योंकि यह सर्दियों के समय में आता है।+1+22012-03-05 18:00:00+01

पोस्टग्रैज पहले ही भूल गए हैं कि यह मूल्य कैसे दर्ज किया गया है। यह सब याद है मूल्य और डेटा प्रकार है। जैसे दशमलव संख्या के साथ। numeric '003.4', numeric '3.40'या numeric '+3.4'- ठीक उसी आंतरिक मूल्य में सभी परिणाम।

AT TIME ZONE

जैसे ही आपको इस तर्क पर पकड़ मिलती है, आप अपनी इच्छानुसार कुछ भी कर सकते हैं। अब जो सब गायब है, वह एक विशिष्ट समय क्षेत्र के अनुसार टाइमस्टैम्प शाब्दिक व्याख्या या प्रतिनिधित्व करने का उपकरण है। यही वह जगह है जहां AT TIME ZONEनिर्माण आता है। दो अलग-अलग उपयोग मामले हैं। timestamptzमें परिवर्तित किया गया है timestampऔर इसके विपरीत।

UTC दर्ज करने के लिए timestamptz 2012-03-05 17:00:00+0:

SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC'

... जो इसके बराबर है:

SELECT timestamptz '2012-03-05 17:00:00 UTC'

उसी समय को EST timestamp(पूर्वी मानक समय) के रूप में प्रदर्शित करने के लिए :

SELECT timestamp '2012-03-05 17:00:00' AT TIME ZONE 'UTC' AT TIME ZONE 'EST'

यह सही है, AT TIME ZONE 'UTC' दो बार । पहले वाला timestampमान UTC टाइमस्टैम्प के प्रकार को देखते हुए (दिए गए) मान की व्याख्या करता है timestamptz। दूसरा एक धर्मान्तरित timestamptzकरने के लिए timestampदिए गए समय क्षेत्र 'ईएसटी' में - क्या समय में इस अनूठी बिंदु पर समय क्षेत्र ईएसटी प्रदर्शित करता है में एक घड़ी।

उदाहरण

SELECT ts AT TIME ZONE 'UTC'
FROM  (
   VALUES
      (1, timestamptz '2012-03-05 17:00:00+0')
    , (2, timestamptz '2012-03-05 18:00:00+1')
    , (3, timestamptz '2012-03-05 17:00:00 UTC')
    , (4, timestamp   '2012-03-05 11:00:00'  AT TIME ZONE '+6') 
    , (5, timestamp   '2012-03-05 17:00:00'  AT TIME ZONE 'UTC') 
    , (6, timestamp   '2012-03-05 07:00:00'  AT TIME ZONE 'US/Hawaii')  -- 
    , (7, timestamptz '2012-03-05 07:00:00 US/Hawaii')                  -- 
    , (8, timestamp   '2012-03-05 07:00:00'  AT TIME ZONE 'HST')        -- 
    , (9, timestamp   '2012-03-05 18:00:00+1')  --  loaded footgun!
      ) t(id, ts);

एक ही UTC टाइमस्टैम्प पकड़े एक टाइमस्टैम्प्ट कॉलम के साथ 8 (या 9) समान पंक्तियाँ लौटाता है 2012-03-05 17:00:00। मेरे समय क्षेत्र में काम करने के लिए 9 वीं पंक्ति होती है, लेकिन एक दुष्ट जाल है। निचे देखो।

Time हवाई समय के लिए समय क्षेत्र नाम और समय क्षेत्र संक्षिप्त नाम के साथ ६ - - - DST (दिन के समय की बचत समय) के अधीन हैं और भिन्न हो सकते हैं, हालांकि वर्तमान में नहीं। जैसे समय क्षेत्र का नाम 'US/Hawaii'डीएसटी नियमों और सभी ऐतिहासिक बदलावों के बारे में स्वचालित रूप से पता है, जबकि एक संक्षिप्त नाम HSTएक निश्चित ऑफसेट के लिए सिर्फ एक गूंगा कोड है। आपको गर्मियों / मानक समय के लिए एक अलग संक्षिप्त नाम जोड़ने की आवश्यकता हो सकती है। नाम सही ढंग से व्याख्या किसी भी दिए गए समय क्षेत्र में टाइमस्टैम्प। एक संक्षिप्त नाम सस्ता है, लेकिन दिए गए टाइमस्टैम्प के लिए सही होना चाहिए:

डेलाइट सेविंग टाइम मानवता के साथ आए सबसे उज्ज्वल विचारों में से नहीं है।

Me रो 9, लोडेड फुटगन के रूप में चिह्नित मेरे लिए काम करता है , लेकिन केवल संयोग से। यदि आप स्पष्ट रूप से एक शाब्दिक कास्ट करते हैं timestamp [without time zone], तो किसी भी समय ज़ोन ऑफसेट को अनदेखा कर दिया जाता है ! केवल नंगे टाइमस्टैम्प का उपयोग किया जाता है। मान तब timestamptzस्तंभ प्रकार से मिलान करने के लिए उदाहरण में स्वचालित रूप से coerced है । इस चरण के लिए, timezoneवर्तमान सत्र की स्थापना मान ली गई है, जो +1मेरे मामले (यूरोप / वियना) में एक ही समय क्षेत्र में होता है । लेकिन शायद आपके मामले में नहीं - जिसके परिणामस्वरूप एक अलग मूल्य होगा। संक्षेप में: timestamptzशाब्दिक कास्ट मत करो timestampया आप टाइम ज़ोन ऑफ़सेट खो देते हैं।

आपके सवाल

उपयोगकर्ता 17 मार्च, 2012, शाम 7 बजे कहते हैं, एक बार स्टोर करता है। मैं टाइमजोन रूपांतरण या समयक्षेत्र संग्रहीत नहीं करना चाहता।

समय क्षेत्र स्वयं कभी संग्रहीत नहीं होता है। UTC टाइमस्टैम्प में प्रवेश करने के लिए ऊपर दी गई विधियों में से एक का उपयोग करें।

मैं केवल उपयोगकर्ताओं को स्थानीय समय क्षेत्र में वर्तमान समय के बाद 'पहले' या 'रिकॉर्ड' प्राप्त करने के लिए निर्दिष्ट समय क्षेत्र का उपयोग करता हूं।

आप अलग-अलग समय क्षेत्रों में सभी ग्राहकों के लिए एक क्वेरी का उपयोग कर सकते हैं।
पूर्ण वैश्विक समय के लिए:

SELECT * FROM tbl WHERE time_col > (now() AT TIME ZONE 'UTC')::time

स्थानीय घड़ी के अनुसार समय के लिए:

SELECT * FROM tbl WHERE time_col > now()::time

पृष्ठभूमि की जानकारी से थके नहीं, फिर भी? मैनुअल में अधिक है।


2
मामूली विस्तार, लेकिन मुझे लगता है कि टाइमस्टैम्प को 2000-01-01 से आंतरिक रूप से माइक्रोसेकंड की संख्या के रूप में संग्रहीत किया जाता है - मैनुअल की तिथि / समय डेटाटाइप अनुभाग देखें । स्रोत के मेरे स्वयं के निरीक्षण इसकी पुष्टि करते हैं। युग के लिए एक अलग मूल का उपयोग करने के लिए अजीब!
हार्मिक

2
@ अलग-अलग युगों के रूप में ... वास्तव में इतना अजीब नहीं है। यह विकिपीडिया पृष्ठ विभिन्न कंप्यूटर प्रणालियों द्वारा उपयोग किए जाने वाले दो दर्जन युगों को सूचीबद्ध करता है। जबकि यूनिक्स युग आम है, यह केवल एक ही नहीं है।
तुलसी बोर्क

4
@ErwinBrandstetter यह एक महान जवाब है, एक गंभीर दोष को छोड़कर। जैसा कि सामंजस्यपूर्ण टिप्पणी की गई है, Postgres यूनिक्स समय का उपयोग नहीं करता है । डॉक्टर के अनुसार : (ए) यूनिक्स के 1970-01-01 के बजाय एपिक 2001-01-01 है, और (बी) जबकि यूनिक्स समय में पूरे सेकंड का रिज़ॉल्यूशन है, पोस्टग्रेज सेकंड के अंशों को बनाए रखता है। भिन्नात्मक अंकों की संख्या संकलन समय विकल्प पर निर्भर करती है: 0 से 6 जब आठ-बाइट पूर्णांक भंडारण (डिफ़ॉल्ट) का उपयोग किया जाता है, या 0 से 10 तक जब फ्लोटिंग-पॉइंट स्टोरेज (पदावनत) का उपयोग किया जाता है।
बेसिल बोर्के

2
@ बासिलबोर्के: मैं इस दुर्भाग्यपूर्ण गलती से अवगत हूं। यदि आपको कोई आपत्ति नहीं है, तो आप इसे संपादित करने के लिए बहुत स्वागत करते हैं। मैंने अतीत में आपके कुछ उत्तर देखे हैं और आप इसमें अच्छे हैं। मेरे द्वारा किया गया एक और संपादन इसे समुदाय विकी के लिए मजबूर करेगा - समय के साथ मैंने इसे स्पष्ट और व्यापक बनाने के लिए बहुत प्रयास (और संपादन) किए हैं।
इरविन ब्रान्डस्टेट्टर

2
सुधार: मेरी पूर्व टिप्पणी पर, मैंने 2001 के बाद के पोस्टग्रुप को गलत बताया। वास्तव में यह 2000 है
बासिल बॉर्क

1

यदि आप डिफ़ॉल्ट रूप से UTC में सौदा करना चाहते हैं:

में config/application.rb, जोड़ें:

config.time_zone = 'UTC'

फिर, यदि आप वर्तमान उपयोगकर्ता टाइमज़ोन नाम संग्रहीत करते हैं, तो आप current_user.timezoneकह सकते हैं।

post.created_at.in_time_zone(current_user.timezone)

current_user.timezoneएक वैध समयक्षेत्र नाम होना चाहिए, अन्यथा आप पूरी सूचीArgumentError: Invalid Timezone देखेंगे ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.