我正在使用vobject在Django中创建ical事件。我在使用较低级别的代码时遇到了麻烦。看来ical正在尝试使用obj.add(TimezoneComponent(tzinfo=getTzid(tzid)))捕获时区。但是后来我从pytz得到raise NonExistentTimeError(dt)。有什么建议吗?在使用变量start1的print语句查看它们时,年,月,日显示正确。

 File "/home/git/chrono/chrono/requests_app/views.py", line 110, in form_valid
    ics_form = create_ics(data)
  File "/home/git/chrono/chrono/requests_app/views.py", line 126, in create_ics
    response = HttpResponse(cal.serialize(), content_type='text/calendar')
  File "/home/one/.virtualenvs/chronos/local/lib/python2.7/site-packages/vobject/base.py", line 186, in serialize
    return behavior.serialize(self, buf, lineLength, validate)
  File "/home/one/.virtualenvs/chronos/local/lib/python2.7/site-packages/vobject/behavior.py", line 147, in serialize
    cls.generateImplicitParameters(obj)
  File "/home/one/.virtualenvs/chronos/local/lib/python2.7/site-packages/vobject/icalendar.py", line 853, in generateImplicitParameters
    obj.add(TimezoneComponent(tzinfo=getTzid(tzid)))
  File "/home/one/.virtualenvs/chronos/local/lib/python2.7/site-packages/vobject/icalendar.py", line 75, in __init__
    self.tzinfo = tzinfo
  File "/home/one/.virtualenvs/chronos/local/lib/python2.7/site-packages/vobject/base.py", line 468, in __setattr__
    prop.fset(self, value)
  File "/home/one/.virtualenvs/chronos/local/lib/python2.7/site-packages/vobject/icalendar.py", line 145, in settzinfo
    transition = getTransition(transitionTo, year, tzinfo)
  File "/home/one/.virtualenvs/chronos/local/lib/python2.7/site-packages/vobject/icalendar.py", line 1856, in getTransition
    uncorrected = firstTransition(generateDates(year, month, day), test)
  File "/home/one/.virtualenvs/chronos/local/lib/python2.7/site-packages/vobject/icalendar.py", line 1816, in firstTransition
    if not test(dt):
  File "/home/one/.virtualenvs/chronos/local/lib/python2.7/site-packages/vobject/icalendar.py", line 1843, in test
    def test(dt): return tzinfo.dst(dt) != zeroDelta
  File "/home/one/.virtualenvs/chronos/local/lib/python2.7/site-packages/pytz/tzinfo.py", line 445, in dst
    dt = self.localize(dt, is_dst)
  File "/home/one/.virtualenvs/chronos/local/lib/python2.7/site-packages/pytz/tzinfo.py", line 327, in localize
    raise NonExistentTimeError(dt)
NonExistentTimeError: 2000-04-02 02:00:00



def create_ics(data):
    start1 = data['date_due']
    print start1.day
    start2 = datetime.datetime(start1.year, start1.month, start1.day)
    start3 = data['action']
    cal = vobject.iCalendar()
    cal.add('method').value = 'PUBLISH'
    vevent = cal.add('vevent')
    vevent.add('dtstart').value = start1
    vevent.add('dtend').value = start2
    vevent.add('dtstamp').value = datetime.datetime.now()
    vevent.add('summary').value = data['action'].name
    response = HttpResponse(cal.serialize(), content_type='text/calendar')
    response['Filename'] = 'filename.ics'
    response['Content-Disposition'] = 'attachment; filename=filename.ics'
    return response


从模型中,日期时间字段:

date_due = models.DateTimeField()


更新:

发现我必须放置:

>>> utc = vobject.icalendar.utc
>>> start = cal.vevent.add('dtstart')
>>> start.value = datetime.datetime(2006, 2, 16, tzinfo = utc)


进去,它起作用了。

最佳答案

简短答案:vobject与pytz不兼容(从0.9.2版本开始)。因此,在尝试序列化它之前,请确保vobject iCalendar中的每个日期时间都已转换为UTC,例如使用.astimezone(pytz.utc)

(这就是我遗忘的每个dtstart,dtend,dtstamp,创建,最后修改以及其他一些vevent字段。)

长答案:vobject尝试在非UTC日期时间做正确的事情,但是在pytz中遇到麻烦。 “正确的事情”来自指定iCalendar的RFC 5545


使用DATE-TIME Form #3“带有本地时间和时区参考的日期”表示datetime。可能类似于DTSTART;TZID=America/New_York:20160714T133000-注意事件的时区的TZID。
为事件中使用的每个唯一TZID,在您的iCalendar中添加一个VTIMEZONE块。这是该时区的完整定义:如何计算可能出现的任何日期时间(包括夏令时规则)相对于UTC的时区偏移量。 (RFC 5545 doesn't specify任何特定的时区名称,因此您必须在iCalendar本身中包括时区定义。vobject会自动为您执行此操作。)


要找出时区转换规则,请使用vobject searches through "all time"(默认年份为2000-2030),以查找时区与UTC的偏移量的变化。这就是问题所在,因为vobject代码无法处理pytz的无效时间错误。

2000年4月2日凌晨2:00是2000年至2030年之间的第一次DST转换,这就是为什么您在该时间遇到错误的原因(即使您自己的代码中没有在任何地方使用它)。

选项:


如果您不想在一天中的特定时间使用(cc)而不是date(如原始问题)。日期没有时区,因此这都不适用。 (vobject处理日期也很好。)
将所有datetime转换为UTC中可识别的日期时间。 UTC不需要VTIMEZONE定义。
使用dateutil timezones代替pytz。例如datetime。由于dateutil是vobject的依赖项,因此我认为这是vobject的VTIMEZONE生成器所针对的格式。 (但尚未进行广泛的测试。此外,gettz需要在计算机上安装tzdb文件,因此并非完全可移植。)
为您使用的每个TZID将自己的VTIMEZONE定义添加到iCalendar中,这应避免在vobject中出现有问题的自动时区生成代码。 (未经测试。在一般情况下很难正确设置。)
提交PR to fix vobject to work with pytz

08-28 00:25