首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何处理重复事件中的DST和TZ?

如何处理重复事件中的DST和TZ?
EN

Stack Overflow用户
提问于 2012-09-20 07:25:38
回答 3查看 4.5K关注 0票数 13

dateutil rrule是否支持DST和TZ?需要类似于iCalendar规则的东西。

如果没有-如何解决此问题(计划重复事件和DST偏移量更改)

导入

代码语言:javascript
复制
>>> from django.utils import timezone
>>> import pytz
>>> from datetime import timedelta
>>> from dateutil import rrule
>>> now = timezone.now()
>>> pl = pytz.timezone("Europe/Warsaw")

时间增量问题(需要具有相同的本地时间,但不同的DST偏移量):

代码语言:javascript
复制
>>> 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有关的问题(需要在每个事件的每个本地小时都具有相同的值):

代码语言:javascript
复制
>>> 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>)
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-09-21 17:24:51

@asdf:我不能将代码添加到评论中,所以我需要将此作为答案发布:

我担心,有了你的解决方案,我将总是丢失DST信息,因此一年中有半年的重复将是1小时的休息时间。

根据你的回答,我发现这可能是正确的解决方案:

代码语言:javascript
复制
>>> 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)

这就是我认为你的解决方案可能失败的原因:

代码语言:javascript
复制
>>> 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结果与我们存储在数据库中的实际数据之间存在1小时的差异。

票数 14
EN

Stack Overflow用户

发布于 2012-09-21 02:46:04

注意,根据您的USE_TZ设置,django.utils.timezone.now()返回的可以是朴素的日期时间,也可以是感知的日期时间。您应该在内部使用什么进行计算(例如,您提供给rrule.rrule的now )是一个基于UTC的日期时间。它可以是偏移量感知的(即,datetime.now(pytz.UTC)),或者是一个幼稚的(即datetime.utcnow())。后者似乎更适合于存储(参见this blogpost)。

现在,rrule.rrule处理时区,这就是为什么您要观察rrule产生的从CEST到CET的变化。但是,如果您想要的是始终获得相同的小时数(例如,每天上午0点,无论是否为DST ),那么您实际上想要“忽略”更改。如果dt是一个可识别的datetime,一种方法是使用dt = dt - dt.dst()

下面是你如何做到这一点:

代码语言:javascript
复制
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设置),两者代表相同的时钟小时。如果要处理aware-UTC-datetime,而不是像示例中那样处理naive-assumed UTC,则只需跳过.replace部分。关于这些转换的快速小抄可以在here上找到。

票数 3
EN

Stack Overflow用户

发布于 2012-09-21 21:56:46

是的,关键是你永远不应该存储localtime。存储UTC并按需转换为本地时间(即在每个请求的基础上,使用请求数据,如Accept-Language头,以了解您应该使用什么tz )。

您所做的是使用本地化的日期时间进行计算(即。rrule.rrule())。这不是最优的,因为您需要知道目标时区才能做到这一点,因此只能针对每个请求执行此操作,而不是预先计算rrule实现。这就是为什么你应该在内部使用UTC (即。以预计算日期时间),然后在发送给用户之前对其进行转换。在这种情况下,只有在接收到请求之后(即,当目标时区已知时)才需要进行转换。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12504247

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档