I am using vobject to create a ical event in Django. I am having trouble with the lower level code. It looks like ical is trying to grab a timezone with obj.add(TimezoneComponent(tzinfo=getTzid(tzid)))
. But then I get raise NonExistentTimeError(dt)
from pytz. Any suggestions on what to do? The year, month, day show correctly as I viewed them with the print statement in for variable start1.
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
from models, the datetime field:
date_due = models.DateTimeField()
UPDATE:
Found I had to place:
>>> utc = vobject.icalendar.utc
>>> start = cal.vevent.add('dtstart')
>>> start.value = datetime.datetime(2006, 2, 16, tzinfo = utc)
into it, which worked.
Short answer: vobject is not (as of 0.9.2) compatible with pytz. So make sure every datetime in your vobject iCalendar has been converted to UTC before trying to serialize it, using something like
.astimezone(pytz.utc)
.(That's every dtstart, dtend, dtstamp, created, last-modified, and maybe some other vevent fields I've forgotten.)
Long answer: vobject tries to do the right thing for non-UTC datetimes, but runs into trouble with pytz. The "right thing" comes from RFC 5545 which specifies iCalendar:
Represent the datetime using DATE-TIME Form #3 "date with local time and time zone reference". That might be something like
DTSTART;TZID=America/New_York:20160714T133000
-- notice the TZID for your event's timezone.Add a VTIMEZONE block to your iCalendar for each unique TZID used in your events. This is a complete definition of that timezone: how to figure out that timezone's offset from UTC for any datetime that might appear, including daylight savings time rules. (RFC 5545 doesn't specify any particular timezone names, so you have to include timezone definitions in the iCalendar itself. vobject does this for you, automatically.)
To figure out the timezone conversion rules, vobject searches through "all time" (default years 2000-2030), looking for changes in the timezone's offset from UTC. And that's where things go wrong, because the vobject code doesn't handle pytz's invalid time errors.
2:00am April 2, 2000 is the first DST transition between 2000-2030, which is why you're getting an error about that time (even though you didn't use it anywhere in your own code).
Options:
date
instead ofdatetime
if you don't want a specific time of day (as in the original question). Dates don't have timezones, so none of this applies. (And vobject handles dates just fine.)datetime
to aware datetimes in UTC. UTC doesn't require a VTIMEZONE definition.from dateutil import tz; ... tzinfo=tz.gettz('America/Los_Angeles')
. Since dateutil is a vobject dependency, I think this is the format vobject's VTIMEZONE generator is designed against. (But haven't tested extensively. Also, gettz requires tzdb files installed on your machine, so isn't entirely portable.)