2012-09-19 22 views
11

क्या डेट्यूटिल क्रूएल डीएसटी और टीजेड का समर्थन करता है? ICalendar RRULE के समान कुछ चाहिए।पुनरावर्ती घटनाओं में डीएसटी और टीजेड को कैसे संभालें?

यदि नहीं - कैसे इस समस्या (शेड्यूलिंग पुनरावर्ती ईवेंट & डीएसटी ऑफसेट बदलें)

आयात

>>> from django.utils import timezone 
>>> import pytz 
>>> from datetime import timedelta 
>>> from dateutil import rrule 
>>> now = timezone.now() 
>>> pl = pytz.timezone("Europe/Warsaw") 

timedelta से संबंधित समस्या से निपटने के लिए (एक ही स्थानीय घंटे की आवश्यकता है, लेकिन अलग अलग डीएसटी ऑफसेट) :

>>> pl.normalize(now) 
datetime.datetime(2012, 9, 20, 1, 16, 58, 226000, tzinfo=<DstTzInfo 'Europe/Warsaw' CEST+2:00:00 DST>)  
>>> pl.normalize(now+timedelta(days=180)) 
datetime.datetime(2013, 3, 19, 0, 16, 58, 226000, tzinfo=<DstTzInfo 'Europe/Warsaw' CET+1:00:00 STD>) 

rrule साथ अंक (एक ही प्रत्येक घटना के हर स्थानीय घंटे की आवश्यकता है):

>>> r = rrule.rrule(3,dtstart=now,interval=180,count=2) 
>>> pl.normalize(r[0]) 
datetime.datetime(2012, 9, 20, 1, 16, 58, tzinfo=<DstTzInfo 'Europe/Warsaw' CEST+2:00:00 DST>) 
>>> pl.normalize(r[1]) 
datetime.datetime(2013, 3, 19, 0, 16, 58, tzinfo=<DstTzInfo 'Europe/Warsaw' CET+1:00:00 STD>) 
+0

डेलाइट बचत और समय क्षेत्र पर सर्वोत्तम प्रथाओं के लिए, यह http://stackoverflow.com/q/2532729/1167333 सर्वोत्तम प्रथाओं का एक अच्छा सारांश देता है – oberron

उत्तर

10

@asdf: मैं तो मैं एक जवाब के रूप में इस पोस्ट करने के लिए की जरूरत है टिप्पणी करने के लिए कोड नहीं जोड़ सकते हैं:

मुझे डर लग रहा है कि अपने समाधान के साथ मैं हमेशा ढीला डीएसटी की जानकारी है, इसलिए साल पुनरावृत्ति का आधा होगा समय से 1 घंटे दूर रहें।

अपने जवाब के आधार पर मुझे पता चला कि यह सही समाधान हो सकता है:

>>> from datetime import datetime 
>>> import pytz 
>>> from dateutil import rrule 
>>> # this is raw data I get from the DB, according to django docs I store it in UTC 
>>> raw = datetime.utcnow().replace(tzinfo=pytz.UTC) 
>>> # in addition I need to store the timezone so I can do dst the calculations 
>>> tz = pytz.timezone("Europe/Warsaw") 
>>> # this means that the actual local time would be 
>>> local = raw.astimezone(tz) 
>>> # but rrule doesn't take into account DST and local time, so I must convert aware datetime to naive 
>>> naive = local.replace(tzinfo=None) 
>>> # standard rrule 
>>> r = rrule.rrule(rrule.DAILY,interval=180,count=10,dtstart=naive) 
>>> for dt in r: 
>>>  # now we must get back to aware datetime - since we are using naive (local) datetime, 
     # we must convert it back to local timezone 
...  print tz.localize(dt) 

यही कारण है कि मुझे लगता है कि अपने समाधान विफल हो सकता है:

>>> from datetime import datetime 
>>> from dateutil import rrule 
>>> import pytz 
>>> now = datetime.utcnow() 
>>> pl = pytz.timezone("Europe/Warsaw") 
>>> r = rrule.rrule(rrule.DAILY, dtstart=now, interval=180, count=2) 
>>> now 
datetime.datetime(2012, 9, 21, 9, 21, 57, 900000) 
>>> for dt in r: 
...  local_dt = dt.replace(tzinfo=pytz.UTC).astimezone(pl) 
...  print local_dt - local_dt.dst() 
...  
2012-09-21 10:21:57+02:00 
2013-03-20 10:21:57+01:00 
>>> # so what is the actual local time we store in the DB ? 
>>> now.replace(tzinfo=pytz.UTC).astimezone(pl) 
datetime.datetime(2012, 9, 21, 11, 21, 57, 900000, tzinfo=<DstTzInfo 'Europe/Warsaw' CEST+2:00:00 DST>) 

जैसा कि आप देख सकते हैं, वहाँ है कठोर परिणाम के बीच 1 घंटे का अंतर, और वास्तविक डेटा जिसे हम डीबी में स्टोर करते हैं।

+0

यह सही लगता है, लेकिन मुझे विश्वास नहीं है कि कोई बेहतर तरीका नहीं है इसे लागू करने के लिए। – Jakobovski

+0

इस दिन मेरे सिर पर टक्कर लगी थी - धन्यवाद @ g00fy! – mstringer

3

ध्यान दें कि क्या django.utils.timezone.now() रिटर्न अपने USE_TZ सेटिंग के आधार पर या तो एक अनुभवहीन या जागरूक datetime हो सकता है। गणना के लिए आंतरिक रूप से आपको क्या उपयोग करना चाहिए (उदाहरण के लिए now जो आप rrule.rrule को प्रदान करते हैं) एक यूटीसी आधारित डेटाटाइम है। यह ऑफसेट-जागरूक हो सकता है (यानी datetime.now(pytz.UTC)), या एक बेवकूफ (यानी datetime.utcnow()) हो सकता है। बाद में संग्रहीत करने के लिए पसंदीदा माना जाता है (देखें this blogpost)।

अब, rrule.rrule टाइमज़ोन हैंडल करता है, यही कारण है कि आप अपने क्रॉल उपज में सीईएसटी-टू-सीईटी परिवर्तन का निरीक्षण करते हैं। हालांकि, अगर आप जो चाहते हैं वह हमेशा एक ही घंटे (उदाहरण के लिए 0 बजे हर दिन, चाहे डीएसटी या नहीं) हो, तो आप वास्तव में परिवर्तन को "अनदेखा" करना चाहते हैं। ऐसा करने का एक तरीका dt = dt - dt.dst() करना होगा, अगर dt एक जागरूक डेटाटाइम था।

यहाँ आप यह कैसे कर सकते हैं:

from datetime import datetime 
from dateutil import rrule 
import pytz 
now = datetime.utcnow() 
pl = pytz.timezone("Europe/Warsaw") 
r = rrule.rrule(rrule.DAILY, dtstart=now, interval=180, count=2) 

# will yield naive datetimes, assumed UTC 
for dt in r: 
    # convert from naive-UTC to aware-local 
    local_dt = dt.replace(tzinfo=pytz.UTC).astimezone(pl) 
    # account for the dst difference 
    print local_dt - local_dt.dst() 

यह दो datetimes प्रिंट, प्रत्येक एक अलग समय क्षेत्र (अच्छी तरह से, अलग डीएसटी सेटिंग) में है, दोनों एक ही wallclock घंटे प्रतिनिधित्व करते हैं। यदि आप उदाहरण के समान बेवकूफ-यूटीसी की बजाय जागरूक-यूटीसी-डेटाटाइम को संभालना चाहते थे, तो आप बस .replace भाग को छोड़ देंगे। इन रूपांतरणों के बारे में एक त्वरित धोखा शीट here पाया जा सकता है।

2

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

आप क्या कर रहे हैं यह है कि आप गणना के लिए स्थानीयकृत डेटाटाइम का उपयोग कर रहे हैं (यानि rrule.rrule())।यह उप-स्थानिक है, क्योंकि आपको ऐसा करने के लिए लक्ष्य टाइमज़ोन जानने की आवश्यकता है, इसलिए यह केवल प्रति अनुरोध किया जा सकता है, क्योंकि क्रुली प्राप्तियों को पूर्ववत करने के विरोध में। यही कारण है कि आपको आंतरिक रूप से यूटीसी का उपयोग करना चाहिए (यानी डेटाटाइम को पूर्वनिर्धारित करने के लिए) और फिर उपयोगकर्ता को भेजने से पहले उन्हें रूपांतरित करें। इस मामले में, केवल अनुरोध प्राप्त करने के बाद ही रूपांतरण करना होगा (यानी, जब लक्ष्य टाइमज़ोन ज्ञात होता है)।