2012-09-19 6 views
11

dateutil rrule은 DST 및 TZ를 지원합니까? iCalendar RRULE과 비슷한 것이 필요합니다.반복 일정에서 DST 및 TZ를 처리하는 방법은 무엇입니까?

하지 않는 경우 -이 문제 (일정 반복 이벤트 & DST 오프셋 변경)

수입 timedelta와

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

문제를 해결하는 방법 (동일한 로컬 시간이 필요하지만, 다른 DST 오프셋) : RRULE와

>>> 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>) 

문제 (각 경우마다 로컬 동일한 시간이 필요)

>>> 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 : 나는 대답으로이를 게시 할 필요가 그래서 나는 의견에 코드를 추가 할 수 없습니다 :

내가 두려워 항상 느슨한 DST 정보 귀하의 솔루션을 나는 것, 올해의 재발 때문에 절반 것 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>) 

당신이 볼 수 있듯이,이 rrule 결과와 우리가 DB에 저장하는 실제 데이터 간의 1 시간 차이.

+0

이 방법이 더 좋을지 모르지만 더 좋은 방법은 없다고 생각합니다. 이것을 구현하기. – Jakobovski

+0

내 하루 종일 머리를 쾅하고 - 감사합니다 @ g00fy! – mstringer

3

django.utils.timezone.now()가 반환하는 것은 USE_TZ 설정에 따라 순진하거나 인식하는 datetime 일 수 있습니다. 계산을 위해 내부적으로 사용해야하는 것 (예 : rrule.rrule에 제공 한 now)은 UTC 기반 datetime입니다. 오프셋 인식 가능 (예 : datetime.now(pytz.UTC)) 또는 순진 오프셋 (예 : datetime.utcnow()) 일 수 있습니다. 후자는 저장에 선호되는 것으로 보인다 (this blogpost 참조).

이제 rrule.rrule이 시간대를 처리하므로 사용자는 자신의 rrule이 산출 한 CEST-CET 변경 사항을 준수해야합니다. 그러나 원하는 시간이 항상 같은 시간 (예 : 매일 오전 0시, DST 여부에 관계없이)을 얻는 것이면 실제로 변경 사항을 "무시"하려는 것입니다. 만약에 dt가 알고있는 datetime 이었다면 이렇게하는 한 가지 방법은 dt = dt - dt.dst() 일 것입니다. 여기

당신이 할 수있는 방법은 다음과 같습니다

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

이 두 날짜 시간을 인쇄, 각각 다른 시간대 (물론, 다른 DST 설정)에, 모두 동일한 wallclock 시간을 나타냅니다. 예제에서와 같이 UTC가 아닌 UTC-datetimes를 처리하는 경우 .replace 부분을 건너 뛰기 만하면됩니다. 이러한 전환에 대한 빠른 치트 시트는 here입니다.

2

예, 중요한 점은 localtime을 절대 저장하면 안된다는 것입니다. UTC를 저장하고 요청시 현지 시간으로 변환하십시오 (즉, 요청 - 기반으로, Accept-Language 헤더와 같은 요청 데이터를 사용하여 어떤 tz를 사용해야하는지 알 수 있습니다).

당신은 계산을 위해 현지화 된 날짜 시간을 사용하고 있습니다 (즉, rrule.rrule()).이를 수행하기위한 목표 시간대를 알아야하기 때문에 차선책입니다. 따라서 rrule 실현을 미리 계산하는 것과는 대조적으로 요청 당 오직 수행 할 수 있습니다. 이것이 UTC를 내부적으로 사용하여 (즉, 날짜 시간을 미리 계산하기 위해) 사용자에게 보내기 전에 변환해야하는 이유입니다. 이 경우 요청을받은 후에 (즉, 목표 시간대를 알고있는 경우) 변환 만 수행하면됩니다.