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

あなたが見ることができるように、そこにありますルルールの結果とDBに格納されている実際のデータとの1時間の違い。

+0

これは正しいようですが、より良い方法はないとは思いませんこれを実装する。 – Jakobovski

+0

は一日中私の頭を叩いていました - ありがとう@ g00fy! – mstringer

3

django.utils.timezone.now()が返すものは、USE_TZの設定に応じて、素朴なものや気づいたものです。あなたが計算のために内部で使うべきもの(例えば、あなたがrrule.rruleに提供するnow)は、UTCベースの日時です。これは、オフセット対応のもの(すなわち、datetime.now(pytz.UTC))でもよいし、ナイーブなもの(datetime.utcnow())でもよい。後者は貯蔵のために好ましいと思われる(this blogpost参照)。

今、rrule.rruleがタイムゾーンを処理するようになったため、あなたは自分のルルールがもたらす利益のCEST-CET変更を観察しています。しかし、あなたが望むものが常に同じ時間を得ることであるならば(例えばDSTに関係なく毎日午前0時)、実際には変更を "無視"したいと思っています。 dtが認識日時であった場合は、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設定)であり、両方とも同じ壁時計の時間を表しています。 例のように、想定されていないUTCではなく認識UTC-datetimesを処理する場合は、単に.replace部分をスキップします。 これらのコンバージョンについての迅速なカンニングシートはhereです。

2

はい、現時点ではlocaltimeを保存しないでください。 UTCを保存し、必要に応じてローカルタイムに変換します(つまり、リクエストごとに、Accept-Languageヘッダーのようなリクエストデータを使用して、何を使用すべきかを知る)。

あなたは、ローカライズされた日時を計算に使用しています(つまり、rrule.rrule())。これは、それを行うための目標タイムゾーンを知る必要があるので、最適ではないので、これは、rruleの実現を事前に計算するのではなく、要求ごとに行うことができます。そのため、UTCを内部的に使用する必要があります(つまり、日付時刻を事前に計算してから、ユーザーに送信する前に変換する必要があります)。この場合、要求を受信した後(つまり、目標のタイムゾーンが分かっている場合)、変換だけを行う必要があります。

関連する問題