13

dateutil rruleはDSTとTZをサポートしていますか?iCalendarRRULEに似たものが必要です。

そうでない場合-この問題に取り組む方法(定期的なイベントのスケジュールとDSTオフセットの変更)

輸入

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

timedeltaの問題(現地時間は同じである必要がありますが、DSTオフセットが異なります):

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

3 に答える 3

15

@asdf: コメントにコードを追加できないため、これを回答として投稿する必要があります:

残念ながら、あなたのソリューションでは常に DST 情報が失われるため、1 年の半分の繰り返しは 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 時間の違いがあります。

于 2012-09-21T09:24:51.210 に答える
3

USE_TZ 設定に応じて、 django.utils.timezone.now() が返すものは、ナイーブまたは認識日時のいずれかになることに注意してください。計算のために内部的に使用する必要があるもの (たとえば、nowrrule.rrule に提供するもの) は、UTC ベースの日時です。これは、オフセットを意識したもの (ie. datetime.now(pytz.UTC)) または単純なもの (ie. datetime.utcnow()) のいずれかです。保存には後者が好まれるようです (このブログ投稿を参照)。

現在、rrule.rrule はタイムゾーンを処理します。これが、rrule が生成するもので CEST から CET への変更を観察する理由です。ただし、常に同じ時間を取得したい場合 (たとえば、DST かどうかに関係なく、毎日午前 0 時)、実際には変更を「無視」する必要があります。これを行う 1 つの方法は、認識されている datetimeである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()

これは 2 つの日付時刻を出力します。それぞれが異なるタイムゾーン (まあ、異なる DST 設定) にあり、両方とも同じ壁時計の時間を表します。例のように naive-assumed-UTC ではなく、aware-UTC-datetimes を処理する場合は、単純に .replace 部分をスキップします。これらの変換に関する簡単なチート シートは、こちらにあります

于 2012-09-20T18:46:04.327 に答える
1

はい、要点は、ローカルタイムを保存してはならないということです。UTC を保存し、オンデマンドで現地時間に変換します (つまり、リクエストごとに、Accept-Language ヘッダーなどのリクエスト データを使用して、どの tz を使用する必要があるかを判断します)。

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

于 2012-09-21T13:56:46.377 に答える