MySQL डेटाइम फ़ील्ड्स और डेलाइट सेविंग टाइम - मैं "अतिरिक्त" घंटा कैसे संदर्भित करूँ?


88

मैं अमेरिका / न्यूयॉर्क समयक्षेत्र का उपयोग कर रहा हूं। फॉल में हम एक घंटे में "फॉल बैक" करते हैं - प्रभावी रूप से 2 बजे एक घंटे। संक्रमण बिंदु पर निम्नलिखित होता है:

यह 01:59:00 -04: 00 है और
फिर 1 मिनट बाद यह बन जाता है:
01:00:00 -05: 00

तो अगर आप बस "1:30 am" कहते हैं, तो यह अस्पष्ट है कि आप पहली बार 1:30 के आसपास या दूसरी बार रोल कर रहे हैं या नहीं। मैं शेड्यूलिंग डेटा को MySQL डेटाबेस में सहेजने की कोशिश कर रहा हूं और यह निर्धारित नहीं कर सकता कि समय को ठीक से कैसे बचाया जाए।

यहाँ समस्या है:
"2009-11-01 00:30:00" को आंतरिक रूप से 2009-11-01 00:30:00 -04: 00
"2009-11-01 01:30:00" के रूप में आंतरिक रूप से संग्रहीत किया जाता है। 2009-11-01 01:30:00 -05: 00

यह ठीक है और काफी अपेक्षित है। लेकिन मैं 01:30:00 -04: 00 में कुछ भी कैसे बचा सकता हूं ? प्रलेखन को निर्दिष्ट ऑफसेट और, तदनुसार, जब मैं निर्दिष्ट करने ऑफसेट विधिवत को नजरअंदाज कर दिया गया है की कोशिश की है के लिए कोई समर्थन नहीं दिखाती है।

एकमात्र समाधान जो मैंने सर्वर को टाइमज़ोन पर सेट करने में शामिल करने के बारे में सोचा है जो डेलाइट बचत समय का उपयोग नहीं करता है और मेरी लिपियों में आवश्यक परिवर्तन कर रहा है (मैं इसके लिए PHP का उपयोग कर रहा हूं)। लेकिन ऐसा नहीं लगता कि यह आवश्यक होना चाहिए।

किसी भी सुझाव के लिए बहुत धन्यवाद।


मैं सुसंगत उत्तर बनाने के लिए MySQL या PHP के बारे में पर्याप्त नहीं जानता, लेकिन मैं शर्त लगाता हूँ कि इसे यूटीसी से और इसके साथ रूपांतरण के साथ कुछ करना होगा।
मार्क रैंसम

2
आंतरिक रूप से वे सभी एक यूटीसी के रूप में संग्रहीत हैं, नहीं?
एली

4
मुझे web.ivy.net/~carton/rant/MySQL-timezones.txt विषय पर एक दिलचस्प पढ़ने को मिला ।
18

अच्छा लिंक, micahwittman - बहुत उपयोगी है।
हारून

महान प्रश्न। एक आम समस्या।
२०:

जवाबों:


47

जब तक आपका सिस्टम UTC या GMT-5 की तरह लगातार ऑफ़सेट टाइमज़ोन पर सेट नहीं होता, तब तक MySQL की तिथि प्रकार स्पष्ट रूप से टूटे हुए हैं और सभी समय को सही ढंग से स्टोर नहीं कर सकते हैं । (मैं MySQL 5.0.45 का उपयोग कर रहा हूं)

ऐसा इसलिए है क्योंकि आप डेलाइट सेविंग टाइम समाप्त होने से पहले घंटे के दौरान किसी भी समय स्टोर नहीं कर सकते । कोई फर्क नहीं पड़ता कि आप इनपुट दिनांक कैसे लेते हैं, प्रत्येक दिनांक फ़ंक्शन इन समयों का इलाज करेगा जैसे कि वे स्विच के बाद घंटे के दौरान हैं।

मेरे सिस्टम का टाइमज़ोन है America/New_York। आइए 1257051600 (सूर्य, 01 नवंबर 2009 06:00:00 +0100) के भंडारण की कोशिश करें।

यहाँ मालिकाना INTERVAL सिंटैक्स का उपयोग किया जा रहा है:

SELECT UNIX_TIMESTAMP('2009-11-01 00:00:00' + INTERVAL 3599 SECOND); # 1257051599
SELECT UNIX_TIMESTAMP('2009-11-01 00:00:00' + INTERVAL 3600 SECOND); # 1257055200

SELECT UNIX_TIMESTAMP('2009-11-01 01:00:00' - INTERVAL 1 SECOND); # 1257051599
SELECT UNIX_TIMESTAMP('2009-11-01 01:00:00' - INTERVAL 0 SECOND); # 1257055200

यहां तक FROM_UNIXTIME()कि सटीक समय भी नहीं लौटाएगा।

SELECT UNIX_TIMESTAMP(FROM_UNIXTIME(1257051599)); # 1257051599
SELECT UNIX_TIMESTAMP(FROM_UNIXTIME(1257051600)); # 1257055200

विचित्र रूप से पर्याप्त है, DATETIME अभी भी स्टोर करेगा और वापस लौटेगा (केवल स्ट्रिंग रूप में!) "खोया" घंटे के भीतर जब DST शुरू होता है (जैसे 2009-03-08 02:59:59)। लेकिन किसी भी MySQL फ़ंक्शन में इन तारीखों का उपयोग करना जोखिम भरा है:

SELECT UNIX_TIMESTAMP('2009-03-08 01:59:59'); # 1236495599
SELECT UNIX_TIMESTAMP('2009-03-08 02:00:00'); # 1236495600
# ...
SELECT UNIX_TIMESTAMP('2009-03-08 02:59:59'); # 1236495600
SELECT UNIX_TIMESTAMP('2009-03-08 03:00:00'); # 1236495600

Takeaway: यदि आपको वर्ष में हर बार स्टोर करने और प्राप्त करने की आवश्यकता है , तो आपके पास कुछ अवांछनीय विकल्प हैं:

  1. GMT + कुछ निरंतर ऑफसेट के लिए सिस्टम टाइमज़ोन सेट करें। जैसे UTC
  2. INT के रूप में स्टोर की तारीखें (जैसा कि हारून ने खोजा, TIMESTAMP भी विश्वसनीय नहीं है)

  3. DATETIME प्रकार को रोकें कुछ निरंतर ऑफसेट समयक्षेत्र है। उदाहरण के लिए, यदि आप MySQL के बाहरAmerica/New_York GMT-5 में अपनी तिथि परिवर्तित करते हैं , तो DATETIME के ​​रूप में संग्रहीत करें (यह आवश्यक हो जाता है: हारून का उत्तर देखें)। तब आपको MySQL की तारीख / समय के कार्यों का उपयोग करने में बहुत सावधानी बरतनी चाहिए, क्योंकि कुछ लोग मान लेते हैं कि सिस्टम टाइमज़ोन के हैं, अन्य (esp। समय अंकगणितीय कार्य) "टाइमज़ोन अज्ञेयवादी" हैं (वे व्यवहार कर सकते हैं जैसे कि समय यूटीसी है)।

हारून और मुझे संदेह है कि ऑटो-जनरेटिंग टाइमस्टैम्प कॉलम भी टूट गए हैं। दोनों 2009-11-01 01:30 -0400और 2009-11-01 01:30 -0500अस्पष्ट रूप में संग्रहीत किया जाएगा 2009-11-01 01:30


इस mrclay पर आपकी सभी मदद के लिए धन्यवाद। आपने यहां की स्थिति को बहुत सटीक रूप से रेखांकित किया है।
हारून

ऐसा प्रतीत होता है कि विकल्प 3 वास्तव में समय अंकगणित के लिए अधिक सुरक्षित है क्योंकि (ऐसा लगता है कि) डीएसटी कार्यक्षमता को जोड़ने से पहले कार्यों को लागू किया गया था। जैसे TIMEDIFF ('2009-11-01 02:30:00', '2009-11-01 00:30:00') 2:00 लौटाता है, जो UTC के लिए सही है, लेकिन अमेरिका / New_York में समय 3 घंटे हैं अलग।
स्टीव क्ले

1
-1: आपने गलती की है कि MySQL दिनांक / समय फ़ंक्शन एक DATETIME प्रकार पर कार्य करता है, जो टाइमज़ोन-अज्ञेयवादी है। इसलिए UNIX_TIMSTAMP में आप जो तर्क दे रहे हैं select '2009-11-01 00:00:00' + INTERVAL 3600 SECOND;वह है '2009-11-01 01:00:00'। UNIX_TIMESTAMP तब केवल सत्र समयक्षेत्र के संदर्भ में UTC को इसे कवर करने का प्रयास करता है - यह उस समयक्षेत्र के DST नियमों के संदर्भ में इसके अतिरिक्त प्रदर्शन करने का प्रयास नहीं करता है।
कोबरा

@kbro ठीक है, लेकिन समस्या बनी हुई है। यदि सत्र tz है America/New_York, तो मुझे 1257051600 स्टोर करने का कोई तरीका नहीं दिख रहा है। क्या आप?
स्टीव क्ले

76

मैं इसे अपने उद्देश्यों के लिए समझ गया हूँ। मैं संक्षेप में बताऊंगा कि मैंने क्या सीखा (क्षमा करें, ये नोट क्रियात्मक हैं; वे मेरे भविष्य के रेफरल के लिए उतने ही हैं जितना कुछ और)।

मैंने अपनी पिछली टिप्पणियों में जो कहा, उसके विपरीत, DATETIME और TIMESTAMP क्षेत्र अलग-अलग व्यवहार करते हैं। TIMESTAMP फ़ील्ड (डॉक्स संकेत के अनुसार) जो भी आप उन्हें "YYYY-MM-DD hh: mm: ss" फॉर्मेट में भेजते हैं और उसे अपने वर्तमान समय-क्षेत्र से बदलकर UTC समय में बदल देते हैं। जब भी आप डेटा पुनर्प्राप्त करते हैं, तो रिवर्स पारदर्शी रूप से होता है। DATETIME फ़ील्ड इस रूपांतरण को नहीं बनाते हैं। वे जो कुछ भी आप उन्हें भेजते हैं और बस सीधे स्टोर करते हैं

न तो DATETIME और न ही TIMESTAMP फ़ील्ड प्रकार DST का अवलोकन करने वाले समयक्षेत्र में डेटा को सही ढंग से संग्रहीत कर सकते हैं । यदि आप "2009-11-01 01:30:00" स्टोर करते हैं, तो खेतों में यह अंतर करने का कोई तरीका नहीं है कि आप 1:30 के किस संस्करण को चाहते हैं - -04: 00 या -05: 00 संस्करण।

ठीक है, इसलिए हमें अपना डेटा एक गैर डीएसटी समयक्षेत्र (जैसे यूटीसी) में संग्रहित करना चाहिए। TIMESTAMP फ़ील्ड इस डेटा को उन कारणों के लिए सटीक रूप से प्रबंधित करने में असमर्थ हैं, जो मैं समझाता हूँ: यदि आपका सिस्टम DST टाइमज़ोन पर सेट है, तो आप TIMESTAMP में जो डालते हैं वह वह नहीं हो सकता है जो आप वापस प्राप्त करते हैं। यहां तक ​​कि अगर आप यह डेटा भेजते हैं, जिसे आपने पहले ही यूटीसी में बदल दिया है, तब भी यह आपके स्थानीय टाइमज़ोन में डेटा को ग्रहण करेगा और यूटीसी के लिए एक और रूपांतरण करेगा। यह TIMESTAMP- लागू स्थानीय-से-UTC-बैक-टू-लोकल राउंडट्रिप हानिपूर्ण है, जब आपका स्थानीय समयक्षेत्र DST ("2009-11-01 01:30:00" मैप्स से 2 अलग-अलग संभावित समय तक) का अवलोकन करता है।

DATETIME के ​​साथ आप अपने डेटा को किसी भी समयक्षेत्र में संग्रहीत कर सकते हैं जिसे आप चाहते हैं और आश्वस्त रहें कि आप जो भी भेजेंगे उसे वापस प्राप्त करेंगे (आप हानिपूर्ण राउंडट्रिप रूपांतरणों में मजबूर नहीं होंगे जो कि TIMESTAMP फ़ील्ड आपके ऊपर फ़िस्ट करते हैं)। तो इसका समाधान यह है कि DATETIME फ़ील्ड का उपयोग करें और अपने सिस्टम टाइम ज़ोन में से जो भी गैर-DST ज़ोन आप इसे सहेजना चाहते हैं , उसे फ़ील्ड में कनवर्ट करने से पहले फ़ील्ड में सहेजें (मुझे लगता है कि UTC शायद सबसे अच्छा विकल्प है)। यह आपको अपनी स्क्रिप्टिंग भाषा में रूपांतरण तर्क बनाने की अनुमति देता है ताकि आप "2009-11-01 01:30:00 -04: 00" या "2009-11-01 01:30" के यूटीसी समकक्ष को स्पष्ट रूप से बचा सकें। 00 -05: 00 "।

ध्यान देने वाली एक और महत्वपूर्ण बात यह है कि यदि आप DST TZ में अपनी तिथियों को संग्रहीत करते हैं तो MySQL की तिथि / समय गणित फ़ंक्शन DST सीमाओं के आसपास ठीक से काम नहीं करते हैं। यूटीसी में बचाने के लिए सभी और अधिक कारण।

संक्षेप में मैं अब यह करता हूं:

डेटाबेस से डेटा प्राप्त करते समय:

एक सटीक यूनिक्स टाइमस्टैम्प प्राप्त करने के लिए MySQL के बाहर UTC के रूप में डेटाबेस से स्पष्ट रूप से डेटा की व्याख्या करें। मैं इसके लिए PHP के strtotime () फ़ंक्शन या उसके DateTime वर्ग का उपयोग करता हूं। इसे MySQL के अंदर MySQL के CONVERT_TZ () या UNIX_TIMESTAMP () फ़ंक्शन का उपयोग करके मज़बूती से नहीं किया जा सकता है क्योंकि CONVERT_TZ केवल 'YYYY-MM-DD hh: mm' ss '' वैल्यू का उत्पादन करेगा जो अस्पष्टता समस्याओं से ग्रस्त है, और UNIX_TIMESTAMP () इसकी मान लेता है इनपुट सिस्टम टाइमज़ोन में है, टाइमज़ोन नहीं डेटा को (UTC) में संग्रहीत किया गया था।

डेटाबेस में डेटा संग्रहीत करते समय:

अपनी तिथि को उस सटीक UTC समय में परिवर्तित करें, जो आप MySQL के बाहर चाहते हैं। उदाहरण के लिए: PHP के डेटाइम क्लास के साथ आप "2009-11-01 1:30:00 ईएसटी" को "2009-11-01 1:30:00 EDT" से विशिष्ट रूप से निर्दिष्ट कर सकते हैं, फिर इसे यूटीसी में बदल सकते हैं और सही यूटीसी समय बचा सकते हैं आपके DATETIME फ़ील्ड में।

ओह। सभी के इनपुट और मदद के लिए बहुत बहुत धन्यवाद। उम्मीद है कि यह किसी और को सड़क के नीचे कुछ सिरदर्द बचाता है।

BTW, मैं इसे MySQL 5.0.22 और 5.0.27 पर देख रहा हूं


13

मुझे लगता है कि micwwittman के लिंक में इन MySQL सीमाओं का सबसे अच्छा व्यावहारिक समाधान है: सत्र टाइमज़ोन को UTC पर सेट करें जब आप लिंक करें:

SET SESSION time_zone = '+0:00'

फिर आप बस इसे यूनिक्स टाइमस्टैम्प भेजें और सब कुछ ठीक होना चाहिए।


यह सलाह ठीक काम करती है। दिए गए कथन के साथ मेरे पूल में सभी कनेक्शनों के प्रवेश के बाद समस्या हल हो गई है।
बर्फानी

4

जब से हम TIMESTAMPस्तंभों का उपयोग करते हैं, तो इस धागे ने मुझे सनकी बना दिया On UPDATE CURRENT_TIMESTAMP:recordTimestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMPबदले हुए रिकॉर्ड और ETL को डेटावेयरहाउस में ट्रैक करने के लिए दिया।

यदि किसी को आश्चर्य होता है, तो इस मामले में, TIMESTAMPसही ढंग से व्यवहार करें और आप दो समान तिथियों के बीच अंतर TIMESTAMPको यूनेस्टिक्स में परिवर्तित कर सकते हैं :

select TestFact.*, UNIX_TIMESTAMP(recordTimestamp) from TestFact;

id  recordTimestamp         UNIX_TIMESTAMP(recordTimestamp)
1   2012-11-04 01:00:10.0   1352005210
2   2012-11-04 01:00:10.0   1352008810

3

लेकिन मैं 01:30:00 -04: 00 में कुछ भी कैसे बचा सकता हूं?

आप यूटीसी में परिवर्तित कर सकते हैं जैसे:

SELECT CONVERT_TZ('2009-11-29 01:30:00','-04:00','+00:00');


इससे भी बेहतर, तारीखों को एक TIMESTAMP क्षेत्र के रूप में सहेजें । यह हमेशा यूटीसी में संग्रहीत होता है, और यूटीसी गर्मियों / सर्दियों के समय के बारे में नहीं जानता है।

CONVERT_TZ का उपयोग करके आप UTC से स्थानीय समय में परिवर्तित कर सकते हैं :

SELECT CONVERT_TZ(UTC_TIMESTAMP(),'+00:00','SYSTEM');

जहाँ '+00: 00' UTC, टाइमज़ोन से है, और 'SYSTEM' OS का स्थानीय टाइमज़ोन है जहाँ MySQL चलता है।


जवाब के लिए धन्यवाद। जैसा कि सबसे अच्छा मैं बता सकता हूं कि डॉक्स के कहने के बावजूद, TIMESTAMP और डेटाटाइम फ़ील्ड समान रूप से व्यवहार कर रहे हैं: वे अपने डेटा को UTC में संग्रहीत करते हैं, लेकिन वे उम्मीद करते हैं कि उनका इनपुट स्थानीय समय में होगा और वे इसे स्वचालित रूप से UTC में परिवर्तित कर देंगे - यदि आप इसे परिवर्तित करते हैं UTC पहले डेटाबेस को इस बात का कोई अंदाजा नहीं था कि मैंने ऐसा किया है और यह 4 (या 5, जो इस बात पर निर्भर करता है कि हम डीएसटी हैं या नहीं) को और अधिक समय तक जोड़ा जाता है। इसलिए समस्या बनी हुई है: मैं 2009-11-01 01:30:00 -04: 00 को इनपुट के रूप में कैसे निर्दिष्ट करूं?
हारून

खैर, मुझे पता चला है कि मेरे अधिकांश भ्रम का स्रोत तथ्य यह है कि UNIX_TIMESTAMP () फ़ंक्शन हमेशा वर्तमान समय क्षेत्र के सापेक्ष इसके दिनांक पैरामीटर की व्याख्या करता है कि क्या आप TIMESTAMP या DATETIME फ़ील्ड से डेटा खींच रहे हैं या नहीं । यह अब समझ में आता है कि मैं इसके बारे में सोचता हूं। मैं बाद में और अपडेट करूंगा।
एरोन

2

माईसकल स्वाभाविक रूप से mysql db से time_zone_name तालिका का उपयोग करके इस समस्या को हल करता है। दिन की बचत के समय की चिंता किए बिना डेटाइम को अपडेट करने के लिए CRUD करते समय CONVERT_TZ का उपयोग करें।

SELECT
  CONVERT_TZ('2019-04-01 00:00:00','Europe/London','UTC') AS time1,
  CONVERT_TZ('2019-03-01 00:00:00','Europe/London','UTC') AS time2;

1

मैं पृष्ठों की यात्राओं की गणना लॉग इन करने और ग्राफ़ में गिनती प्रदर्शित करने (प्लॉट jQuery प्लगइन का उपयोग करके) पर काम कर रहा था। मैंने परीक्षण डेटा के साथ तालिका भरी और सब कुछ ठीक लग रहा था, लेकिन मैंने देखा कि ग्राफ के अंत में अंक एक्स-अक्ष पर लेबल के अनुसार एक दिन बंद थे। परीक्षा के बाद मैंने देखा कि दिन 2015-10-25 के लिए काउंट व्यू को डेटाबेस से दो बार पुनर्प्राप्त किया गया था और फ़्लॉट को पास किया गया था, इसलिए इस तारीख के बाद हर दिन एक दिन के लिए सही हो गया।
थोड़ी देर के लिए अपने कोड में बग की तलाश करने के बाद मुझे महसूस हुआ कि यह तारीख तब है जब डीएसटी होता है। तब मैं इस SO पृष्ठ पर आया ...
... लेकिन सुझाए गए समाधानों के लिए एक ओवरकिल था जो मुझे चाहिए था या उनके पास अन्य नुकसान थे। मैं अस्पष्ट टाइमस्टैम्प के बीच अंतर नहीं कर पाने के बारे में बहुत चिंतित नहीं हूं। मुझे केवल प्रति दिन रिकॉर्ड गिनने और प्रदर्शित करने की आवश्यकता है।

सबसे पहले, मैं तिथि सीमा पुनः प्राप्त करता हूं:

SELECT 
    DATE(MIN(created_timestamp)) AS min_date, 
    DATE(MAX(created_timestamp)) AS max_date 
FROM page_display_log
WHERE item_id = :item_id

फिर, एक लूप में, एक दिन के चरण के min_dateसाथ समाप्त होने के साथ शुरू max_date( 60*60*24), मैं पुनः प्राप्त कर रहा हूं:

for( $day = $min_date_timestamp; $day <= $max_date_timestamp; $day += 60 * 60 * 24 ) {
    $query = "
        SELECT COUNT(*) AS count_per_day
        FROM page_display_log
        WHERE 
            item_id = :item_id AND
            ( 
                created_timestamp BETWEEN 
                '" . date( "Y-m-d 00:00:00", $day ) . "' AND
                '" . date( "Y-m-d 23:59:59", $day ) . "'
            )
    ";
    //execute query and do stuff with the result
}

मेरे अंतिम और त्वरित समाधान करने के लिए मेरी समस्या यह थी:

$min_date_timestamp += 60 * 60 * 2; // To avoid DST problems
for( $day = $min_date_timestamp; $day <= $max_da.....

इसलिए मैं दिन की शुरुआत में लूप को घूर नहीं रहा हूं , लेकिन दो घंटे बाद । दिन अभी भी वही है, और मैं अभी भी सही मायने निकाल रहा हूं, क्योंकि मैं स्पष्ट रूप से दिन के 00:00:00 और 23:59:59 के बीच के रिकॉर्ड के लिए डेटाबेस पूछता हूं, टाइमस्टैम्प के वास्तविक समय की परवाह किए बिना। और जब समय एक घंटे से कम हो जाता है, तो मैं अभी भी सही दिन में हूं।

नोट: मुझे पता है कि यह 5 साल पुराना धागा है, और मुझे पता है कि यह ओपीएस सवाल का जवाब नहीं है, लेकिन यह मेरे जैसे लोगों की मदद कर सकता है, जिन्होंने इस पृष्ठ का सामना किया था जो मेरे द्वारा बताई गई समस्या के समाधान की तलाश में था।


शायद वास्तविक प्रश्न के लिए प्रासंगिक नहीं है, लेकिन यह बहुत ही अयोग्य है, और किसी को भी इसकी नकल नहीं करनी चाहिए! इसके बजाय, एक एकल क्वेरी जारी करें जैसे:
Done

"SELECT CAST(created_timestamp AS date) day,COUNT(*) WHERE item_id=:item_id AND (created_timestamp BETWEEN '".date("Y-m-d 00:00:00", $min_date_timestamp)."' AND '".date("Y-m-d 23:59:59", $max_date_timestamp)."') GROUP BY day ORDER BY day";
चल रहा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.