kopia lustrzana https://github.com/collective/icalendar
force UTC conversion for CREATED, DTSTAMP and LAST-MODIFIED properties
rodzic
346d79df5b
commit
f0cc686290
|
@ -5,6 +5,11 @@ Changelog
|
|||
3.1 (unreleased)
|
||||
----------------
|
||||
|
||||
* When using Component.add() to add icalendar properties, force a value
|
||||
conversion to UTC for CREATED, DTSTART and LAST-MODIFIED. The RFC expects UTC
|
||||
for those properties.
|
||||
[thet]
|
||||
|
||||
* Removed last occurrences of old API (from_string).
|
||||
[Rembane]
|
||||
|
||||
|
|
|
@ -9,6 +9,9 @@ These are the defined components.
|
|||
|
||||
"""
|
||||
|
||||
import pytz
|
||||
from datetime import datetime
|
||||
|
||||
# from python
|
||||
from types import ListType, TupleType
|
||||
SequenceTypes = (ListType, TupleType)
|
||||
|
@ -24,7 +27,7 @@ from icalendar.prop import TypesFactory, vText
|
|||
# The component factory
|
||||
|
||||
class ComponentFactory(CaselessDict):
|
||||
""" All components defined in rfc 2445 are registered in this factory
|
||||
""" All components defined in rfc 2445 are registered in this factory
|
||||
class. To get a component you can use it like this.
|
||||
|
||||
>>> factory = ComponentFactory()
|
||||
|
@ -225,10 +228,14 @@ class Component(CaselessDict):
|
|||
# handling of property values
|
||||
|
||||
def _encode(self, name, value, cond=1):
|
||||
# internal, for conditional convertion of values.
|
||||
""" Conditional convertion of values.
|
||||
|
||||
"""
|
||||
|
||||
if cond:
|
||||
klass = types_factory.for_property(name)
|
||||
return klass(value)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
|
@ -238,9 +245,41 @@ class Component(CaselessDict):
|
|||
else:
|
||||
self[name] = self._encode(name, value, encode)
|
||||
|
||||
|
||||
def add(self, name, value, encode=1):
|
||||
"If property exists append, else create and set it"
|
||||
""" Add a property.
|
||||
|
||||
Test the for timezone correctness: dtstart should preserve it's
|
||||
timezone, crated, dtstamp and last-modified must be in UTC.
|
||||
>>> from datetime import datetime
|
||||
>>> import pytz
|
||||
>>> from icalendar.cal import Component
|
||||
>>> comp = Component()
|
||||
>>> comp.add('dtstart', datetime(2010,10,10,10,0,0,tzinfo=pytz.timezone("Europe/Vienna")))
|
||||
>>> comp.add('created', datetime(2010,10,10,12,0,0))
|
||||
>>> comp.add('dtstamp', datetime(2010,10,10,14,0,0,tzinfo=pytz.timezone("Europe/Vienna")))
|
||||
>>> comp.add('last-modified', datetime(2010,10,10,16,0,0,tzinfo=pytz.utc))
|
||||
|
||||
>>> lines = comp.to_ical().splitlines()
|
||||
>>> "DTSTART;TZID=Europe/Vienna;VALUE=DATE-TIME:20101010T100000" in lines
|
||||
True
|
||||
>>> "CREATED;VALUE=DATE-TIME:20101010T120000Z" in lines
|
||||
True
|
||||
>>> "DTSTAMP;VALUE=DATE-TIME:20101010T130000Z" in lines
|
||||
True
|
||||
>>> "LAST-MODIFIED;VALUE=DATE-TIME:20101010T160000Z" in lines
|
||||
True
|
||||
|
||||
"""
|
||||
if isinstance(value, datetime) and\
|
||||
name.lower() in ('dtstamp', 'created', 'last-modified'):
|
||||
# RFC expects UTC for those... force value conversion.
|
||||
if getattr(value, 'tzinfo', False) and value.tzinfo is not None:
|
||||
value = value.astimezone(pytz.utc)
|
||||
else:
|
||||
# assume UTC for naive datetime instances
|
||||
value = pytz.utc.localize(value)
|
||||
|
||||
# If property exists append, else create and set it.
|
||||
if name in self:
|
||||
oldval = self[name]
|
||||
value = self._encode(name, value, encode)
|
||||
|
@ -250,7 +289,8 @@ class Component(CaselessDict):
|
|||
self.set(name, [oldval, value], encode=0)
|
||||
else:
|
||||
self.set(name, value, encode)
|
||||
if getattr(value, 'tzinfo', False) and value.tzinfo is not None:
|
||||
if getattr(value, 'tzinfo', False) and value.tzinfo is not None and value.tzinfo is not pytz.utc:
|
||||
# set the timezone as a parameter to the property
|
||||
tzid = value.tzinfo.zone
|
||||
self[name].params.update({'TZID': tzid})
|
||||
|
||||
|
|
|
@ -37,13 +37,13 @@ class TestTimezoned(unittest.TestCase):
|
|||
|
||||
event = icalendar.Event()
|
||||
tz = pytz.timezone("Europe/Vienna")
|
||||
event.add('dtstart', datetime.datetime(2012,02,13,10,00,00,tzinfo=tz))
|
||||
event.add('dtend', datetime.datetime(2012,02,17,18,00,00,tzinfo=tz))
|
||||
event.add('dtstamp', datetime.datetime(2010,10,10,10,10,10,tzinfo=tz))
|
||||
event.add('created', datetime.datetime(2010,10,10,10,10,10,tzinfo=tz))
|
||||
event.add('uid', u'123456')
|
||||
event.add('last-modified', datetime.datetime(2010,10,10,10,10,10,tzinfo=tz))
|
||||
event.add('summary', u'artsprint 2012')
|
||||
event.add('dtstart', datetime.datetime(2012,02,13,10,00,00,tzinfo=tz))
|
||||
event.add('dtend', datetime.datetime(2012,02,17,18,00,00,tzinfo=tz))
|
||||
#event.add('rrule', u'FREQ=YEARLY;INTERVAL=1;COUNT=10')
|
||||
event.add('description', u'sprinting at the artsprint')
|
||||
event.add('location', u'aka bild, wien')
|
||||
|
@ -60,3 +60,7 @@ class TestTimezoned(unittest.TestCase):
|
|||
|
||||
self.assertTrue("DTSTART;TZID=Europe/Vienna;VALUE=DATE-TIME:20120213T100000" in ical_lines)
|
||||
self.assertTrue("ATTENDEE:sepp" in ical_lines)
|
||||
|
||||
# ical standard expects DTSTAMP and CREATED in UTC
|
||||
self.assertTrue("DTSTAMP;VALUE=DATE-TIME:20101010T091010Z" in ical_lines)
|
||||
self.assertTrue("CREATED;VALUE=DATE-TIME:20101010T091010Z" in ical_lines)
|
||||
|
|
Ładowanie…
Reference in New Issue