# -*- coding: utf-8 -*- from __future__ import print_function import datetime import dateutil import re import sys import unittest import json from dateutil.tz import tzutc from dateutil.rrule import rrule, rruleset, WEEKLY, MONTHLY from vobject import base, iCalendar from vobject import icalendar from vobject.base import __behaviorRegistry as behavior_registry from vobject.base import ContentLine, parseLine, ParseError from vobject.base import readComponents, textLineToContentLine from vobject.change_tz import change_tz from vobject.icalendar import MultiDateBehavior, PeriodBehavior, \ RecurringComponent, utc from vobject.icalendar import parseDtstart, stringToTextValues, \ stringToPeriod, timedeltaToString two_hours = datetime.timedelta(hours=2) def get_test_file(path): """ Helper function to open and read test files. """ filepath = "test_files/{}".format(path) if sys.version_info[0] < 3: # On python 2, this library operates on bytes. f = open(filepath, 'r') else: # On python 3, it operates on unicode. We need to specify an encoding # for systems for which the preferred encoding isn't utf-8 (e.g windows) f = open(filepath, 'r', encoding='utf-8') text = f.read() f.close() return text class TestCalendarSerializing(unittest.TestCase): """ Test creating an iCalendar file """ max_diff = None def test_scratchbuild(self): """ CreateCalendar 2.0 format from scratch """ test_cal = get_test_file("simple_2_0_test.ics") cal = base.newFromBehavior('vcalendar', '2.0') cal.add('vevent') cal.vevent.add('dtstart').value = datetime.datetime(2006, 5, 9) cal.vevent.add('description').value = "Test event" cal.vevent.add('created').value = \ datetime.datetime(2006, 1, 1, 10, tzinfo=dateutil.tz.tzical( "test_files/timezones.ics").get('US/Pacific')) cal.vevent.add('uid').value = "Not very random UID" # Note we're normalizing line endings, because no one got time for that. self.assertEqual( cal.serialize().replace('\r\n', '\n'), test_cal.replace('\r\n', '\n') ) def test_unicode(self): """ Test unicode characters """ test_cal = get_test_file("utf8_test.ics") vevent = base.readOne(test_cal).vevent vevent2 = base.readOne(vevent.serialize()) self.assertEqual(str(vevent), str(vevent2)) self.assertEqual( vevent.summary.value, 'The title こんにちはキティ' ) if sys.version_info[0] < 3: test_cal = test_cal.decode('utf-8') vevent = base.readOne(test_cal).vevent vevent2 = base.readOne(vevent.serialize()) self.assertEqual(str(vevent), str(vevent2)) self.assertEqual( vevent.summary.value, 'The title こんにちはキティ' ) def test_wrapping(self): """ Should support input file with a long text field covering multiple lines """ test_journal = get_test_file("journal.ics") vobj = base.readOne(test_journal) vjournal = base.readOne(vobj.serialize()) self.assertTrue('Joe, Lisa, and Bob' in vjournal.description.value) self.assertTrue('Tuesday.\n2.' in vjournal.description.value) def test_multiline(self): """ Multi-text serialization test """ category = base.newFromBehavior('categories') category.value = ['Random category'] self.assertEqual( category.serialize().strip(), "CATEGORIES:Random category" ) category.value.append('Other category') self.assertEqual( category.serialize().strip(), "CATEGORIES:Random category,Other category" ) def test_semicolon_separated(self): """ Semi-colon separated multi-text serialization test """ request_status = base.newFromBehavior('request-status') request_status.value = ['5.1', 'Service unavailable'] self.assertEqual( request_status.serialize().strip(), "REQUEST-STATUS:5.1;Service unavailable" ) @staticmethod def test_unicode_multiline(): """ Test multiline unicode characters """ cal = iCalendar() cal.add('method').value = 'REQUEST' cal.add('vevent') cal.vevent.add('created').value = datetime.datetime.now() cal.vevent.add('summary').value = 'Классное событие' cal.vevent.add('description').value = ('Классное событие Классное событие Классное событие Классное событие ' 'Классное событие Классsdssdное событие') # json tries to encode as utf-8 and it would break if some chars could not be encoded json.dumps(cal.serialize()) @staticmethod def test_ical_to_hcal(): """ Serializing iCalendar to hCalendar. Since Hcalendar is experimental and the behavior doesn't seem to want to load, This test will have to wait. tzs = dateutil.tz.tzical("test_files/timezones.ics") cal = base.newFromBehavior('hcalendar') self.assertEqual( str(cal.behavior), "" ) cal.add('vevent') cal.vevent.add('summary').value = "this is a note" cal.vevent.add('url').value = "http://microformats.org/code/hcalendar/creator" cal.vevent.add('dtstart').value = datetime.date(2006,2,27) cal.vevent.add('location').value = "a place" cal.vevent.add('dtend').value = datetime.date(2006,2,27) + datetime.timedelta(days = 2) event2 = cal.add('vevent') event2.add('summary').value = "Another one" event2.add('description').value = "The greatest thing ever!" event2.add('dtstart').value = datetime.datetime(1998, 12, 17, 16, 42, tzinfo = tzs.get('US/Pacific')) event2.add('location').value = "somewhere else" event2.add('dtend').value = event2.dtstart.value + datetime.timedelta(days = 6) hcal = cal.serialize() """ #self.assertEqual( # str(hcal), # """ # # this is a note: # Monday, February 27 # - Tuesday, February 28 # at a place # # # # Another one: # Thursday, December 17, 16:42 # - Wednesday, December 23, 16:42 # at somewhere else #
The greatest thing ever!
#
# """ #) class TestBehaviors(unittest.TestCase): """ Test Behaviors """ def test_general_behavior(self): """ Tests for behavior registry, getting and creating a behavior. """ # Check expected behavior registry. self.assertEqual( sorted(behavior_registry.keys()), ['', 'ACTION', 'ADR', 'AVAILABLE', 'BUSYTYPE', 'CALSCALE', 'CATEGORIES', 'CLASS', 'COMMENT', 'COMPLETED', 'CONTACT', 'CREATED', 'DAYLIGHT', 'DESCRIPTION', 'DTEND', 'DTSTAMP', 'DTSTART', 'DUE', 'DURATION', 'EXDATE', 'EXRULE', 'FN', 'FREEBUSY', 'LABEL', 'LAST-MODIFIED', 'LOCATION', 'METHOD', 'N', 'ORG', 'PHOTO', 'PRODID', 'RDATE', 'RECURRENCE-ID', 'RELATED-TO', 'REQUEST-STATUS', 'RESOURCES', 'RRULE', 'STANDARD', 'STATUS', 'SUMMARY', 'TRANSP', 'TRIGGER', 'UID', 'VALARM', 'VAVAILABILITY', 'VCALENDAR', 'VCARD', 'VEVENT', 'VFREEBUSY', 'VJOURNAL', 'VTIMEZONE', 'VTODO'] ) # test get_behavior behavior = base.getBehavior('VCALENDAR') self.assertEqual( str(behavior), "" ) self.assertTrue(behavior.isComponent) self.assertEqual( base.getBehavior("invalid_name"), None ) # test for ContentLine (not a component) non_component_behavior = base.getBehavior('RDATE') self.assertFalse(non_component_behavior.isComponent) def test_MultiDateBehavior(self): """ Test MultiDateBehavior """ parseRDate = MultiDateBehavior.transformToNative self.assertEqual( str(parseRDate(textLineToContentLine("RDATE;VALUE=DATE:19970304,19970504,19970704,19970904"))), "" ) self.assertEqual( str(parseRDate(textLineToContentLine("RDATE;VALUE=PERIOD:19960403T020000Z/19960403T040000Z,19960404T010000Z/PT3H"))), "" ) def test_periodBehavior(self): """ Test PeriodBehavior """ line = ContentLine('test', [], '', isNative=True) line.behavior = PeriodBehavior line.value = [(datetime.datetime(2006, 2, 16, 10), two_hours)] self.assertEqual( line.transformFromNative().value, '20060216T100000/PT2H' ) self.assertEqual( line.transformToNative().value, [(datetime.datetime(2006, 2, 16, 10, 0), datetime.timedelta(0, 7200))] ) line.value.append((datetime.datetime(2006, 5, 16, 10), two_hours)) self.assertEqual( line.serialize().strip(), 'TEST:20060216T100000/PT2H,20060516T100000/PT2H' ) class TestVTodo(unittest.TestCase): """ VTodo Tests """ def test_vtodo(self): """ Test VTodo """ vtodo = get_test_file("vtodo.ics") obj = base.readOne(vtodo) obj.vtodo.add('completed') obj.vtodo.completed.value = datetime.datetime(2015,5,5,13,30) self.assertEqual(obj.vtodo.completed.serialize()[0:23], 'COMPLETED:20150505T1330') obj = base.readOne(obj.serialize()) self.assertEqual(obj.vtodo.completed.value, datetime.datetime(2015,5,5,13,30)) class TestVobject(unittest.TestCase): """ VObject Tests """ max_diff = None @classmethod def setUpClass(cls): """ Method for setting up class fixture before running tests in the class. Fetches test file. """ cls.simple_test_cal = get_test_file("simple_test.ics") def test_readComponents(self): """ Test if reading components correctly """ cal = next(readComponents(self.simple_test_cal)) self.assertEqual(str(cal), "]>]>") self.assertEqual(str(cal.vevent.summary), "") def test_parseLine(self): """ Test line parsing """ self.assertEqual(parseLine("BLAH:"), ('BLAH', [], '', None)) self.assertEqual( parseLine("RDATE:VALUE=DATE:19970304,19970504,19970704,19970904"), ('RDATE', [], 'VALUE=DATE:19970304,19970504,19970704,19970904', None) ) self.assertEqual( parseLine('DESCRIPTION;ALTREP="http://www.wiz.org":The Fall 98 Wild Wizards Conference - - Las Vegas, NV, USA'), ('DESCRIPTION', [['ALTREP', 'http://www.wiz.org']], 'The Fall 98 Wild Wizards Conference - - Las Vegas, NV, USA', None) ) self.assertEqual( parseLine("EMAIL;PREF;INTERNET:john@nowhere.com"), ('EMAIL', [['PREF'], ['INTERNET']], 'john@nowhere.com', None) ) self.assertEqual( parseLine('EMAIL;TYPE="blah",hah;INTERNET="DIGI",DERIDOO:john@nowhere.com'), ('EMAIL', [['TYPE', 'blah', 'hah'], ['INTERNET', 'DIGI', 'DERIDOO']], 'john@nowhere.com', None) ) self.assertEqual( parseLine('item1.ADR;type=HOME;type=pref:;;Reeperbahn 116;Hamburg;;20359;'), ('ADR', [['type', 'HOME'], ['type', 'pref']], ';;Reeperbahn 116;Hamburg;;20359;', 'item1') ) self.assertRaises(ParseError, parseLine, ":") class TestGeneralFileParsing(unittest.TestCase): """ General tests for parsing ics files. """ def test_readOne(self): """ Test reading first component of ics """ cal = get_test_file("silly_test.ics") silly = base.readOne(cal) self.assertEqual( str(silly), ", , ]>" ) self.assertEqual( str(silly.stuff), "" ) def test_importing(self): """ Test importing ics """ cal = get_test_file("standard_test.ics") c = base.readOne(cal, validate=True) self.assertEqual( str(c.vevent.valarm.trigger), "" ) self.assertEqual( str(c.vevent.dtstart.value), "2002-10-28 14:00:00-08:00" ) self.assertTrue( isinstance(c.vevent.dtstart.value, datetime.datetime) ) self.assertEqual( str(c.vevent.dtend.value), "2002-10-28 15:00:00-08:00" ) self.assertTrue( isinstance(c.vevent.dtend.value, datetime.datetime) ) self.assertEqual( c.vevent.dtstamp.value, datetime.datetime(2002, 10, 28, 1, 17, 6, tzinfo=tzutc()) ) vevent = c.vevent.transformFromNative() self.assertEqual( str(vevent.rrule), "" ) def test_bad_stream(self): """ Test bad ics stream """ cal = get_test_file("badstream.ics") self.assertRaises(ParseError, base.readOne, cal) def test_bad_line(self): """ Test bad line in ics file """ cal = get_test_file("badline.ics") self.assertRaises(ParseError, base.readOne, cal) newcal = base.readOne(cal, ignoreUnreadable=True) self.assertEqual( str(newcal.vevent.x_bad_underscore), '' ) def test_parseParams(self): """ Test parsing parameters """ self.assertEqual( base.parseParams(';ALTREP="http://www.wiz.org"'), [['ALTREP', 'http://www.wiz.org']] ) self.assertEqual( base.parseParams(';ALTREP="http://www.wiz.org;;",Blah,Foo;NEXT=Nope;BAR'), [['ALTREP', 'http://www.wiz.org;;', 'Blah', 'Foo'], ['NEXT', 'Nope'], ['BAR']] ) class TestVcards(unittest.TestCase): """ Test VCards """ @classmethod def setUpClass(cls): """ Method for setting up class fixture before running tests in the class. Fetches test file. """ cls.test_file = get_test_file("vcard_with_groups.ics") cls.card = base.readOne(cls.test_file) def test_vcard_creation(self): """ Test creating a vCard """ vcard = base.newFromBehavior('vcard', '3.0') self.assertEqual( str(vcard), "" ) def test_default_behavior(self): """ Default behavior test. """ card = self.card self.assertEqual( base.getBehavior('note'), None ) self.assertEqual( str(card.note.value), "The Mayor of the great city of Goerlitz in the great country of Germany.\nNext line." ) def test_with_groups(self): """ vCard groups test """ card = self.card self.assertEqual( str(card.group), 'home' ) self.assertEqual( str(card.tel.group), 'home' ) card.group = card.tel.group = 'new' self.assertEqual( str(card.tel.serialize().strip()), 'new.TEL;TYPE=fax,voice,msg:+49 3581 123456' ) self.assertEqual( str(card.serialize().splitlines()[0]), 'new.BEGIN:VCARD' ) def test_vcard_3_parsing(self): """ VCARD 3.0 parse test """ test_file = get_test_file("simple_3_0_test.ics") card = base.readOne(test_file) # value not rendering correctly? #self.assertEqual( # card.adr.value, # "" #) self.assertEqual( card.org.value, ["University of Novosibirsk", "Department of Octopus Parthenogenesis"] ) for _ in range(3): new_card = base.readOne(card.serialize()) self.assertEqual(new_card.org.value, card.org.value) card = new_card class TestIcalendar(unittest.TestCase): """ Tests for icalendar.py """ max_diff = None def test_parseDTStart(self): """ Should take a content line and return a datetime object. """ self.assertEqual( parseDtstart(textLineToContentLine("DTSTART:20060509T000000")), datetime.datetime(2006, 5, 9, 0, 0) ) def test_regexes(self): """ Test regex patterns """ self.assertEqual( re.findall(base.patterns['name'], '12foo-bar:yay'), ['12foo-bar', 'yay'] ) self.assertEqual( re.findall(base.patterns['safe_char'], 'a;b"*,cd'), ['a', 'b', '*', 'c', 'd'] ) self.assertEqual( re.findall(base.patterns['qsafe_char'], 'a;b"*,cd'), ['a', ';', 'b', '*', ',', 'c', 'd'] ) self.assertEqual( re.findall(base.patterns['param_value'], '"quoted";not-quoted;start"after-illegal-quote', re.VERBOSE), ['"quoted"', '', 'not-quoted', '', 'start', '', 'after-illegal-quote', ''] ) match = base.line_re.match('TEST;ALTREP="http://www.wiz.org":value:;"') self.assertEqual( match.group('value'), 'value:;"' ) self.assertEqual( match.group('name'), 'TEST' ) self.assertEqual( match.group('params'), ';ALTREP="http://www.wiz.org"' ) def test_stringToTextValues(self): """ Test string lists """ self.assertEqual( stringToTextValues(''), [''] ) self.assertEqual( stringToTextValues('abcd,efgh'), ['abcd', 'efgh'] ) def test_stringToPeriod(self): """ Test datetime strings """ self.assertEqual( stringToPeriod("19970101T180000Z/19970102T070000Z"), (datetime.datetime(1997, 1, 1, 18, 0, tzinfo=tzutc()), datetime.datetime(1997, 1, 2, 7, 0, tzinfo=tzutc())) ) self.assertEqual( stringToPeriod("19970101T180000Z/PT1H"), (datetime.datetime(1997, 1, 1, 18, 0, tzinfo=tzutc()), datetime.timedelta(0, 3600)) ) def test_timedeltaToString(self): """ Test timedelta strings """ self.assertEqual( timedeltaToString(two_hours), 'PT2H' ) self.assertEqual( timedeltaToString(datetime.timedelta(minutes=20)), 'PT20M' ) def test_vtimezone_creation(self): """ Test timezones """ tzs = dateutil.tz.tzical("test_files/timezones.ics") pacific = icalendar.TimezoneComponent(tzs.get('US/Pacific')) self.assertEqual( str(pacific), ">" ) santiago = icalendar.TimezoneComponent(tzs.get('Santiago')) self.assertEqual( str(santiago), ">" ) for year in range(2001, 2010): for month in (2, 9): dt = datetime.datetime(year, month, 15, tzinfo=tzs.get('Santiago')) self.assertTrue(dt.replace(tzinfo=tzs.get('Santiago')), dt) @staticmethod def test_timezone_serializing(): """ Serializing with timezones test """ tzs = dateutil.tz.tzical("test_files/timezones.ics") pacific = tzs.get('US/Pacific') cal = base.Component('VCALENDAR') cal.setBehavior(icalendar.VCalendar2_0) ev = cal.add('vevent') ev.add('dtstart').value = datetime.datetime(2005, 10, 12, 9, tzinfo=pacific) evruleset = rruleset() evruleset.rrule(rrule(WEEKLY, interval=2, byweekday=[2,4], until=datetime.datetime(2005, 12, 15, 9))) evruleset.rrule(rrule(MONTHLY, bymonthday=[-1,-5])) evruleset.exdate(datetime.datetime(2005, 10, 14, 9, tzinfo=pacific)) ev.rruleset = evruleset ev.add('duration').value = datetime.timedelta(hours=1) apple = tzs.get('America/Montreal') ev.dtstart.value = datetime.datetime(2005, 10, 12, 9, tzinfo=apple) def test_pytz_timezone_serializing(self): """ Serializing with timezones from pytz test """ try: import pytz except ImportError: return self.skipTest("pytz not installed") # NOQA # Avoid conflicting cached tzinfo from other tests def unregister_tzid(tzid): """Clear tzid from icalendar TZID registry""" if icalendar.getTzid(tzid, False): icalendar.registerTzid(tzid, None) unregister_tzid('US/Eastern') eastern = pytz.timezone('US/Eastern') cal = base.Component('VCALENDAR') cal.setBehavior(icalendar.VCalendar2_0) ev = cal.add('vevent') ev.add('dtstart').value = eastern.localize( datetime.datetime(2008, 10, 12, 9)) serialized = cal.serialize() expected_vtimezone = get_test_file("tz_us_eastern.ics") self.assertIn( expected_vtimezone.replace('\r\n', '\n'), serialized.replace('\r\n', '\n') ) # Exhaustively test all zones (just looking for no errors) for tzname in pytz.all_timezones: unregister_tzid(tzname) tz = icalendar.TimezoneComponent(tzinfo=pytz.timezone(tzname)) tz.serialize() def test_freeBusy(self): """ Test freebusy components """ test_cal = get_test_file("freebusy.ics") vfb = base.newFromBehavior('VFREEBUSY') vfb.add('uid').value = 'test' vfb.add('dtstart').value = datetime.datetime(2006, 2, 16, 1, tzinfo=utc) vfb.add('dtend').value = vfb.dtstart.value + two_hours vfb.add('freebusy').value = [(vfb.dtstart.value, two_hours / 2)] vfb.add('freebusy').value = [(vfb.dtstart.value, vfb.dtend.value)] self.assertEqual( vfb.serialize().replace('\r\n', '\n'), test_cal.replace('\r\n', '\n') ) def test_availablity(self): """ Test availability components """ test_cal = get_test_file("availablity.ics") vcal = base.newFromBehavior('VAVAILABILITY') vcal.add('uid').value = 'test' vcal.add('dtstamp').value = datetime.datetime(2006, 2, 15, 0, tzinfo=utc) vcal.add('dtstart').value = datetime.datetime(2006, 2, 16, 0, tzinfo=utc) vcal.add('dtend').value = datetime.datetime(2006, 2, 17, 0, tzinfo=utc) vcal.add('busytype').value = "BUSY" av = base.newFromBehavior('AVAILABLE') av.add('uid').value = 'test1' av.add('dtstamp').value = datetime.datetime(2006, 2, 15, 0, tzinfo=utc) av.add('dtstart').value = datetime.datetime(2006, 2, 16, 9, tzinfo=utc) av.add('dtend').value = datetime.datetime(2006, 2, 16, 12, tzinfo=utc) av.add('summary').value = "Available in the morning" vcal.add(av) self.assertEqual( vcal.serialize().replace('\r\n', '\n'), test_cal.replace('\r\n', '\n') ) def test_recurrence(self): """ Ensure date valued UNTILs in rrules are in a reasonable timezone, and include that day (12/28 in this test) """ test_file = get_test_file("recurrence.ics") cal = base.readOne(test_file) dates = list(cal.vevent.getrruleset()) self.assertEqual( dates[0], datetime.datetime(2006, 1, 26, 23, 0, tzinfo=tzutc()) ) self.assertEqual( dates[1], datetime.datetime(2006, 2, 23, 23, 0, tzinfo=tzutc()) ) self.assertEqual( dates[-1], datetime.datetime(2006, 12, 28, 23, 0, tzinfo=tzutc()) ) def test_recurring_component(self): """ Test recurring events """ vevent = RecurringComponent(name='VEVENT') # init self.assertTrue(vevent.isNative) # rruleset should be None at this point. # No rules have been passed or created. self.assertEqual(vevent.rruleset, None) # Now add start and rule for recurring event vevent.add('dtstart').value = datetime.datetime(2005, 1, 19, 9) vevent.add('rrule').value =u"FREQ=WEEKLY;COUNT=2;INTERVAL=2;BYDAY=TU,TH" self.assertEqual( list(vevent.rruleset), [datetime.datetime(2005, 1, 20, 9, 0), datetime.datetime(2005, 2, 1, 9, 0)] ) self.assertEqual( list(vevent.getrruleset(addRDate=True)), [datetime.datetime(2005, 1, 19, 9, 0), datetime.datetime(2005, 1, 20, 9, 0)] ) # Also note that dateutil will expand all-day events (datetime.date values) # to datetime.datetime value with time 0 and no timezone. vevent.dtstart.value = datetime.date(2005,3,18) self.assertEqual( list(vevent.rruleset), [datetime.datetime(2005, 3, 29, 0, 0), datetime.datetime(2005, 3, 31, 0, 0)] ) self.assertEqual( list(vevent.getrruleset(True)), [datetime.datetime(2005, 3, 18, 0, 0), datetime.datetime(2005, 3, 29, 0, 0)] ) class TestChangeTZ(unittest.TestCase): """ Tests for change_tz.change_tz """ class StubCal(object): class StubEvent(object): class Node(object): def __init__(self, value): self.value = value def __init__(self, dtstart, dtend): self.dtstart = self.Node(dtstart) self.dtend = self.Node(dtend) def __init__(self, dates): """ dates is a list of tuples (dtstart, dtend) """ self.vevent_list = [self.StubEvent(*d) for d in dates] def test_change_tz(self): """ Change the timezones of events in a component to a different timezone """ # Setup - create a stub vevent list old_tz = dateutil.tz.gettz('UTC') # 0:00 new_tz = dateutil.tz.gettz('America/Chicago') # -5:00 dates = [ (datetime.datetime(1999, 12, 31, 23, 59, 59, 0, tzinfo=old_tz), datetime.datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=old_tz)), (datetime.datetime(2010, 12, 31, 23, 59, 59, 0, tzinfo=old_tz), datetime.datetime(2011, 1, 2, 3, 0, 0, 0, tzinfo=old_tz))] cal = self.StubCal(dates) # Exercise - change the timezone change_tz(cal, new_tz, dateutil.tz.gettz('UTC')) # Test - that the tzs were converted correctly expected_new_dates = [ (datetime.datetime(1999, 12, 31, 17, 59, 59, 0, tzinfo=new_tz), datetime.datetime(1999, 12, 31, 18, 0, 0, 0, tzinfo=new_tz)), (datetime.datetime(2010, 12, 31, 17, 59, 59, 0, tzinfo=new_tz), datetime.datetime(2011, 1, 1, 21, 0, 0, 0, tzinfo=new_tz))] for vevent, expected_datepair in zip(cal.vevent_list, expected_new_dates): self.assertEqual(vevent.dtstart.value, expected_datepair[0]) self.assertEqual(vevent.dtend.value, expected_datepair[1]) def test_change_tz_utc_only(self): """ Change any UTC timezones of events in a component to a different timezone """ # Setup - create a stub vevent list utc_tz = dateutil.tz.gettz('UTC') # 0:00 non_utc_tz = dateutil.tz.gettz('America/Santiago') # -4:00 new_tz = dateutil.tz.gettz('America/Chicago') # -5:00 dates = [ (datetime.datetime(1999, 12, 31, 23, 59, 59, 0, tzinfo=utc_tz), datetime.datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=non_utc_tz))] cal = self.StubCal(dates) # Exercise - change the timezone passing utc_only=True change_tz(cal, new_tz, dateutil.tz.gettz('UTC'), utc_only=True) # Test - that only the utc item has changed expected_new_dates = [ (datetime.datetime(1999, 12, 31, 17, 59, 59, 0, tzinfo=new_tz), dates[0][1])] for vevent, expected_datepair in zip(cal.vevent_list, expected_new_dates): self.assertEqual(vevent.dtstart.value, expected_datepair[0]) self.assertEqual(vevent.dtend.value, expected_datepair[1]) def test_change_tz_default(self): """ Change the timezones of events in a component to a different timezone, passing a default timezone that is assumed when the events don't have one """ # Setup - create a stub vevent list new_tz = dateutil.tz.gettz('America/Chicago') # -5:00 dates = [ (datetime.datetime(1999, 12, 31, 23, 59, 59, 0, tzinfo=None), datetime.datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=None))] cal = self.StubCal(dates) # Exercise - change the timezone change_tz(cal, new_tz, dateutil.tz.gettz('UTC')) # Test - that the tzs were converted correctly expected_new_dates = [ (datetime.datetime(1999, 12, 31, 17, 59, 59, 0, tzinfo=new_tz), datetime.datetime(1999, 12, 31, 18, 0, 0, 0, tzinfo=new_tz))] for vevent, expected_datepair in zip(cal.vevent_list, expected_new_dates): self.assertEqual(vevent.dtstart.value, expected_datepair[0]) self.assertEqual(vevent.dtend.value, expected_datepair[1]) if __name__ == '__main__': unittest.main()