ri_cal 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. data/History.txt +45 -0
  2. data/Manifest.txt +129 -0
  3. data/README.txt +394 -0
  4. data/Rakefile +31 -0
  5. data/bin/ri_cal +8 -0
  6. data/component_attributes/alarm.yml +10 -0
  7. data/component_attributes/calendar.yml +4 -0
  8. data/component_attributes/component_property_defs.yml +180 -0
  9. data/component_attributes/event.yml +45 -0
  10. data/component_attributes/freebusy.yml +16 -0
  11. data/component_attributes/journal.yml +35 -0
  12. data/component_attributes/timezone.yml +3 -0
  13. data/component_attributes/timezone_period.yml +11 -0
  14. data/component_attributes/todo.yml +46 -0
  15. data/copyrights.txt +1 -0
  16. data/docs/draft-ietf-calsify-2446bis-08.txt +7280 -0
  17. data/docs/draft-ietf-calsify-rfc2445bis-09.txt +10416 -0
  18. data/docs/incrementers.txt +7 -0
  19. data/docs/rfc2445.pdf +0 -0
  20. data/lib/ri_cal.rb +144 -0
  21. data/lib/ri_cal/component.rb +247 -0
  22. data/lib/ri_cal/component/alarm.rb +21 -0
  23. data/lib/ri_cal/component/calendar.rb +219 -0
  24. data/lib/ri_cal/component/event.rb +60 -0
  25. data/lib/ri_cal/component/freebusy.rb +18 -0
  26. data/lib/ri_cal/component/journal.rb +30 -0
  27. data/lib/ri_cal/component/t_z_info_timezone.rb +123 -0
  28. data/lib/ri_cal/component/timezone.rb +196 -0
  29. data/lib/ri_cal/component/timezone/daylight_period.rb +25 -0
  30. data/lib/ri_cal/component/timezone/standard_period.rb +23 -0
  31. data/lib/ri_cal/component/timezone/timezone_period.rb +53 -0
  32. data/lib/ri_cal/component/todo.rb +43 -0
  33. data/lib/ri_cal/core_extensions.rb +6 -0
  34. data/lib/ri_cal/core_extensions/array.rb +7 -0
  35. data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
  36. data/lib/ri_cal/core_extensions/date.rb +13 -0
  37. data/lib/ri_cal/core_extensions/date/conversions.rb +61 -0
  38. data/lib/ri_cal/core_extensions/date_time.rb +15 -0
  39. data/lib/ri_cal/core_extensions/date_time/conversions.rb +50 -0
  40. data/lib/ri_cal/core_extensions/object.rb +8 -0
  41. data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
  42. data/lib/ri_cal/core_extensions/string.rb +8 -0
  43. data/lib/ri_cal/core_extensions/string/conversions.rb +63 -0
  44. data/lib/ri_cal/core_extensions/time.rb +13 -0
  45. data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
  46. data/lib/ri_cal/core_extensions/time/conversions.rb +61 -0
  47. data/lib/ri_cal/core_extensions/time/tzid_access.rb +50 -0
  48. data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +88 -0
  49. data/lib/ri_cal/floating_timezone.rb +32 -0
  50. data/lib/ri_cal/invalid_property_value.rb +8 -0
  51. data/lib/ri_cal/invalid_timezone_identifer.rb +20 -0
  52. data/lib/ri_cal/occurrence_enumerator.rb +206 -0
  53. data/lib/ri_cal/occurrence_period.rb +17 -0
  54. data/lib/ri_cal/parser.rb +138 -0
  55. data/lib/ri_cal/properties/alarm.rb +390 -0
  56. data/lib/ri_cal/properties/calendar.rb +164 -0
  57. data/lib/ri_cal/properties/event.rb +1526 -0
  58. data/lib/ri_cal/properties/freebusy.rb +594 -0
  59. data/lib/ri_cal/properties/journal.rb +1240 -0
  60. data/lib/ri_cal/properties/timezone.rb +151 -0
  61. data/lib/ri_cal/properties/timezone_period.rb +416 -0
  62. data/lib/ri_cal/properties/todo.rb +1562 -0
  63. data/lib/ri_cal/property_value.rb +149 -0
  64. data/lib/ri_cal/property_value/array.rb +27 -0
  65. data/lib/ri_cal/property_value/cal_address.rb +11 -0
  66. data/lib/ri_cal/property_value/date.rb +175 -0
  67. data/lib/ri_cal/property_value/date_time.rb +335 -0
  68. data/lib/ri_cal/property_value/date_time/additive_methods.rb +44 -0
  69. data/lib/ri_cal/property_value/date_time/time_machine.rb +181 -0
  70. data/lib/ri_cal/property_value/date_time/timezone_support.rb +96 -0
  71. data/lib/ri_cal/property_value/duration.rb +110 -0
  72. data/lib/ri_cal/property_value/geo.rb +11 -0
  73. data/lib/ri_cal/property_value/integer.rb +12 -0
  74. data/lib/ri_cal/property_value/occurrence_list.rb +144 -0
  75. data/lib/ri_cal/property_value/period.rb +82 -0
  76. data/lib/ri_cal/property_value/recurrence_rule.rb +145 -0
  77. data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +97 -0
  78. data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +79 -0
  79. data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +148 -0
  80. data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +53 -0
  81. data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +31 -0
  82. data/lib/ri_cal/property_value/recurrence_rule/occurence_incrementer.rb +793 -0
  83. data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +131 -0
  84. data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +60 -0
  85. data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +33 -0
  86. data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +49 -0
  87. data/lib/ri_cal/property_value/recurrence_rule/validations.rb +125 -0
  88. data/lib/ri_cal/property_value/text.rb +40 -0
  89. data/lib/ri_cal/property_value/uri.rb +11 -0
  90. data/lib/ri_cal/property_value/utc_offset.rb +33 -0
  91. data/lib/ri_cal/required_timezones.rb +55 -0
  92. data/ri_cal.gemspec +49 -0
  93. data/sample_ical_files/from_ical_dot_app/test1.ics +38 -0
  94. data/script/console +10 -0
  95. data/script/destroy +14 -0
  96. data/script/generate +14 -0
  97. data/script/txt2html +71 -0
  98. data/spec/ri_cal/component/alarm_spec.rb +12 -0
  99. data/spec/ri_cal/component/calendar_spec.rb +54 -0
  100. data/spec/ri_cal/component/event_spec.rb +601 -0
  101. data/spec/ri_cal/component/freebusy_spec.rb +12 -0
  102. data/spec/ri_cal/component/journal_spec.rb +37 -0
  103. data/spec/ri_cal/component/t_z_info_timezone_spec.rb +36 -0
  104. data/spec/ri_cal/component/timezone_spec.rb +218 -0
  105. data/spec/ri_cal/component/todo_spec.rb +112 -0
  106. data/spec/ri_cal/component_spec.rb +224 -0
  107. data/spec/ri_cal/core_extensions/string/conversions_spec.rb +78 -0
  108. data/spec/ri_cal/core_extensions/time/calculations_spec.rb +188 -0
  109. data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +45 -0
  110. data/spec/ri_cal/occurrence_enumerator_spec.rb +573 -0
  111. data/spec/ri_cal/parser_spec.rb +303 -0
  112. data/spec/ri_cal/property_value/date_spec.rb +53 -0
  113. data/spec/ri_cal/property_value/date_time_spec.rb +383 -0
  114. data/spec/ri_cal/property_value/duration_spec.rb +126 -0
  115. data/spec/ri_cal/property_value/occurrence_list_spec.rb +72 -0
  116. data/spec/ri_cal/property_value/period_spec.rb +49 -0
  117. data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +21 -0
  118. data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1814 -0
  119. data/spec/ri_cal/property_value/text_spec.rb +25 -0
  120. data/spec/ri_cal/property_value/utc_offset_spec.rb +48 -0
  121. data/spec/ri_cal/property_value_spec.rb +125 -0
  122. data/spec/ri_cal/required_timezones_spec.rb +67 -0
  123. data/spec/ri_cal_spec.rb +53 -0
  124. data/spec/spec.opts +4 -0
  125. data/spec/spec_helper.rb +46 -0
  126. data/tasks/gem_loader/load_active_support.rb +3 -0
  127. data/tasks/gem_loader/load_tzinfo_gem.rb +2 -0
  128. data/tasks/ri_cal.rake +410 -0
  129. data/tasks/spec.rake +50 -0
  130. metadata +221 -0
@@ -0,0 +1,78 @@
1
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
2
+
3
+ require File.join(File.dirname(__FILE__), %w[.. .. .. spec_helper])
4
+
5
+ describe RiCal::CoreExtensions::String::Conversions do
6
+ context "#to_ri_cal_date_time_value" do
7
+
8
+ it "should produce a DateTime property for a valid RFC 2445 datetime string" do
9
+ "20090304T123456".to_ri_cal_date_time_value.should == RiCal::PropertyValue::DateTime.new(nil, :value => "20090304T123456")
10
+ end
11
+
12
+ it "should produce a DateTime property for a valid RFC 2445 datetime string with a TZID parameter" do
13
+ "TZID=America/New_York:20090304T123456".to_ri_cal_date_time_value.should == RiCal::PropertyValue::DateTime.new(nil, :params => {"TZID" => "America/New_York"}, :value => "20090304T123456")
14
+ end
15
+
16
+ it "should raise an InvalidPropertyValue error if the string is not a valid RFC 2445 datetime string" do
17
+ lambda {"20090304".to_ri_cal_date_time_value}.should raise_error(RiCal::InvalidPropertyValue)
18
+ end
19
+ end
20
+
21
+ context "#to_ri_cal_duration_value" do
22
+
23
+ it "should produce a Duration property for a valid RFC 2445 duration string" do
24
+ "P1H".to_ri_cal_duration_value.should == RiCal::PropertyValue::Duration.new(nil, :value => "P1H")
25
+ end
26
+
27
+ it "should raise an InvalidPropertyValue error if the string is not a valid RFC 2445 datetime string" do
28
+ lambda {"20090304".to_ri_cal_duration_value}.should raise_error(RiCal::InvalidPropertyValue)
29
+ end
30
+ end
31
+
32
+ context "#to_ri_cal_date_or_date_time_value" do
33
+
34
+ it "should produce a DateTime property for a valid RFC 2445 datetime string" do
35
+ "20090304T123456".to_ri_cal_date_or_date_time_value.should == RiCal::PropertyValue::DateTime.new(nil, :value => "20090304T123456")
36
+ end
37
+
38
+ it "should produce a DateTime property for a valid RFC 2445 datetime string with a TZID parameter" do
39
+ "TZID=America/New_York:20090304T123456".to_ri_cal_date_or_date_time_value.should == RiCal::PropertyValue::DateTime.new(nil, :params => {"TZID" => "America/New_York"}, :value => "20090304T123456")
40
+ end
41
+
42
+ it "should produce a Date property for a valid RFC 2445 date string" do
43
+ "20090304".to_ri_cal_date_or_date_time_value.should == RiCal::PropertyValue::Date.new(nil, :value => "20090304")
44
+ end
45
+
46
+
47
+ it "should raise an InvalidPropertyValue error if the string is not a valid RFC 2445 date or datetime string" do
48
+ lambda {"2009/03/04".to_ri_cal_date_or_date_time_value}.should raise_error(RiCal::InvalidPropertyValue)
49
+ end
50
+ end
51
+
52
+ context "#to_ri_cal_occurrence_list_value" do
53
+
54
+ it "should produce a DateTime property for a valid RFC 2445 datetime string" do
55
+ "20090304T123456".to_ri_cal_occurrence_list_value.should == RiCal::PropertyValue::DateTime.new(nil, :value => "20090304T123456")
56
+ end
57
+
58
+ it "should produce a DateTime property for a valid RFC 2445 datetime string with a TZID parameter" do
59
+ "TZID=America/New_York:20090304T123456".to_ri_cal_occurrence_list_value.should == RiCal::PropertyValue::DateTime.new(nil, :params => {"TZID" => "America/New_York"}, :value => "20090304T123456")
60
+ end
61
+
62
+ it "should produce a Date property for a valid RFC 2445 date string" do
63
+ "20090304".to_ri_cal_occurrence_list_value.should == RiCal::PropertyValue::Date.new(nil, :value => "20090304")
64
+ end
65
+
66
+ it "should produce a Period property for a valid RFC 2445 period string (two time format)" do
67
+ "20090304T012345/20090304T023456".to_ri_cal_occurrence_list_value.should == RiCal::PropertyValue::Period.new(nil, :value => "20090304T012345/20090304T023456")
68
+ end
69
+
70
+ it "should produce a Period property for a valid RFC 2445 period string (time and duration format)" do
71
+ "20090304T012345/P1H".to_ri_cal_occurrence_list_value.should == RiCal::PropertyValue::Period.new(nil, :value => "20090304T012345/P1H")
72
+ end
73
+
74
+ it "should raise an InvalidPropertyValue error if the string is not a valid RFC 2445 date or datetime string" do
75
+ lambda {"foobar".to_ri_cal_date_or_date_time_value}.should raise_error(RiCal::InvalidPropertyValue)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,188 @@
1
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
2
+
3
+ require File.join(File.dirname(__FILE__), %w[.. .. .. spec_helper])
4
+
5
+ describe RiCal::CoreExtensions::Time::Calculations do
6
+
7
+ describe ".iso_week_num" do
8
+
9
+ it "should calculate week 1 for January 1, 2001 for a wkst of 1 (Monday)" do
10
+ Date.new(2001, 1,1).iso_week_num(1).should == 1
11
+ end
12
+
13
+ it "should calculate week 1 for January 7, 2001 for a wkst of 1 (Monday)" do
14
+ Date.new(2001, 1,7).iso_week_num(1).should == 1
15
+ end
16
+
17
+ it "should calculate week 2 for January 8, 2001 for a wkst of 1 (Monday)" do
18
+ Date.new(2001, 1,8).iso_week_num(1).should == 2
19
+ end
20
+
21
+ it "should calculate week 52 for December 31, 2000 for a wkst of 1 (Monday)" do
22
+ Date.new(2000, 12,31).iso_week_num(1).should == 52
23
+ end
24
+
25
+ it "should calculate week 52 for January 1, 2001 for a wkst of 2 (Tuesday)" do
26
+ Date.new(2001, 1, 1).iso_week_num(2).should == 52
27
+ end
28
+
29
+ it "should calculate week 1 for Dec 31, 2003 for a wkst of 1 (Monday)" do
30
+ Date.new(2003, 12, 31).iso_week_num(1).should == 1
31
+ end
32
+ end
33
+
34
+ describe ".iso_year" do
35
+ it "should be 1999 for January 2 2000" do
36
+ Date.new(2000, 1, 2).iso_year(1).should == 1999
37
+ end
38
+
39
+ it "should be 1998 for December 29, 1997" do
40
+ Date.new(1997, 12, 29).iso_year(1).should == 1998
41
+ end
42
+ end
43
+
44
+ describe ".iso_year_start" do
45
+
46
+ it "should calculate January 4 1999 for January 2 2000 for a wkst of 1 (Monday)" do
47
+ Date.new(2000, 1, 2).iso_year_start(1).to_s.should == Date.new(1999, 1, 4).to_s
48
+ end
49
+
50
+ it "should calculate January 3 2000 for January 3 2000 for a wkst of 1 (Monday)" do
51
+ Date.new(2000, 1, 3).iso_year_start(1).to_s.should == Date.new(2000, 1, 3).to_s
52
+ end
53
+
54
+ it "should calculate January 3 2000 for January 4 2000 for a wkst of 1 (Monday)" do
55
+ Date.new(2000, 1, 4).iso_year_start(1).to_s.should == Date.new(2000, 1, 3).to_s
56
+ end
57
+
58
+ it "should calculate January 3 2000 for December 31 2000 for a wkst of 1 (Monday)" do
59
+ Date.new(2000, 12, 31).iso_year_start(1).to_s.should == Date.new(2000, 1, 3).to_s
60
+ end
61
+
62
+ it "should calculate week January 1, 2001 for January 1, 2001 for a wkst of 1 (Monday)" do
63
+ Date.new(2001, 1,1).iso_year_start(1).should == Date.new(2001, 1, 1)
64
+ end
65
+
66
+ it "should calculate week January 1, 2001 for July 4, 2001 for a wkst of 1 (Monday)" do
67
+ Date.new(2001, 7,4).iso_year_start(1).should == Date.new(2001, 1, 1)
68
+ end
69
+
70
+ it "should calculate January 3 2000 for January 3 2000" do
71
+ Date.new(2000, 1, 3).iso_year_start(1).to_s.should == Date.new(2000, 1, 3).to_s
72
+ end
73
+
74
+ it "should calculate January 3 2000 for January 4 2000" do
75
+ Date.new(2000, 1, 4).iso_year_start(1).to_s.should == Date.new(2000, 1, 3).to_s
76
+ end
77
+
78
+ # it "should calculate week 1 for January 7, 2001 for a wkst of 1 (Monday)" do
79
+ # Date.new(2001, 1,7).iso_week_num(1).should == 1
80
+ # end
81
+ #
82
+ # it "should calculate week 2 for January 8, 2001 for a wkst of 1 (Monday)" do
83
+ # Date.new(2001, 1,8).iso_week_num(1).should == 2
84
+ # end
85
+ #
86
+ # it "should calculate week 52 for December 31, 2000 for a wkst of 1 (Monday)" do
87
+ # Date.new(2000, 12,31).iso_week_num(1).should == 52
88
+ # end
89
+ #
90
+ # it "should calculate week 52 for January 1, 2001 for a wkst of 2 (Tuesday)" do
91
+ # Date.new(2001, 1, 1).iso_week_num(2).should == 52
92
+ # end
93
+ #
94
+ # it "should calculate week 1 for Dec 31, 2003 for a wkst of 1 (Monday)" do
95
+ # Date.new(2003, 12, 31).iso_week_num(1).should == 1
96
+ # end
97
+ end
98
+
99
+ describe "#iso_week_one" do
100
+
101
+ before(:each) do
102
+ @it = RiCal::CoreExtensions::Time::Calculations
103
+ end
104
+
105
+ describe "with a monday week start" do
106
+ it "should return Jan 3, 2000 for 2000" do
107
+ @it.iso_week_one(2000, 1).should == Date.new(2000, 1, 3)
108
+ end
109
+
110
+ it "should return Jan 1, 2001 for 2001" do
111
+ @it.iso_week_one(2001, 1).should == Date.new(2001, 1,1)
112
+ end
113
+
114
+ it "should return Dec 31, 2001 for 2002" do
115
+ @it.iso_week_one(2002, 1).should == Date.new(2001, 12, 31)
116
+ end
117
+
118
+ it "should return Dec 30, 2002 for 2003" do
119
+ @it.iso_week_one(2003, 1).should == Date.new(2002, 12, 30)
120
+ end
121
+
122
+ it "should return Dec 29, 2003 for 2004" do
123
+ @it.iso_week_one(2004, 1).should == Date.new(2003, 12, 29)
124
+ end
125
+ end
126
+
127
+ it "should return Jan 2, 2001 for 2001 with a Tuesday week start" do
128
+ @it.iso_week_one(2001, 2).should == Date.new(2001, 1, 2)
129
+ end
130
+
131
+ it "should return Jan 3, 2001 for 2001 with a Wednesday week start" do
132
+ @it.iso_week_one(2001, 3).should == Date.new(2001, 1, 3)
133
+ end
134
+
135
+ it "should return Jan 4, 2001 for 2001 with a Thursday week start" do
136
+ @it.iso_week_one(2001, 4).should == Date.new(2001, 1, 4)
137
+ end
138
+
139
+ it "should return Dec 29, 2000 for 2001 with a Friday week start" do
140
+ @it.iso_week_one(2001, 5).should == Date.new(2000, 12, 29)
141
+ end
142
+
143
+ it "should return Dec 30, 2000 for 2001 with a Saturday week start" do
144
+ @it.iso_week_one(2001, 6).should == Date.new(2000, 12, 30)
145
+ end
146
+
147
+ it "should return Dec 31, 2000 for 2001 with a Sunday week start" do
148
+ @it.iso_week_one(2001, 0).should == Date.new(2000, 12, 31)
149
+ end
150
+ end
151
+
152
+ describe ".leap_year?" do
153
+ it "should return true for 2000" do
154
+ Date.parse("1/3/2000").should be_leap_year
155
+ end
156
+
157
+ it "should return false for 2007" do
158
+ Date.parse("1/3/2007").should_not be_leap_year
159
+ end
160
+
161
+ it "should return true for 2008" do
162
+ Date.parse("1/3/2008").should be_leap_year
163
+ end
164
+
165
+ it "should return false for 2100" do
166
+ Date.parse("1/3/2100").should_not be_leap_year
167
+ end
168
+ end
169
+
170
+ describe ".days_in_month" do
171
+
172
+ it "should return 29 for February in a leap year" do
173
+ Date.new(2008, 2, 1).days_in_month.should == 29
174
+ end
175
+
176
+ it "should return 28 for February in a non-leap year" do
177
+ Date.new(2009, 2, 1).days_in_month.should == 28
178
+ end
179
+
180
+ it "should return 31 for January in a leap year" do
181
+ Date.new(2008, 1, 1).days_in_month.should == 31
182
+ end
183
+
184
+ it "should return 31 for January in a non-leap year" do
185
+ Date.new(2009, 1, 1).days_in_month.should == 31
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,45 @@
1
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
2
+
3
+ require File.join(File.dirname(__FILE__), %w[.. .. .. spec_helper])
4
+
5
+ describe RiCal::CoreExtensions::Time::WeekDayPredicates do
6
+
7
+ describe ".nth_wday_in_month" do
8
+ it "should return Feb 28, 2005 for the 4th Monday for a date in February 2005" do
9
+ expected = RiCal::PropertyValue::Date.new(nil, :value => "20050228")
10
+ it =Date.parse("Feb 7, 2005").nth_wday_in_month(4, 1)
11
+ it.should == expected
12
+ end
13
+ end
14
+
15
+ describe ".nth_wday_in_month?" do
16
+ it "should return true for Feb 28, 2005 for the 4th Monday" do
17
+ Date.parse("Feb 28, 2005").nth_wday_in_month?(4, 1).should be
18
+ end
19
+ end
20
+
21
+ describe ".start_of_week_with_wkst" do
22
+ describe "for Wednesday Sept 10 1997" do
23
+ before(:each) do
24
+ @date = Date.parse("Sept 10, 1997")
25
+ end
26
+
27
+ it "should return a Date of Sept 7, 1997 00:00 for a wkst of 0 - Sunday" do
28
+ @date.start_of_week_with_wkst(0).should == Date.parse("Sept 7, 1997")
29
+ end
30
+
31
+ it "should return a Date of Sept 8, 1997 00:00 for a wkst of 1 - Monday" do
32
+ @date.start_of_week_with_wkst(1).should == Date.parse("Sept 8, 1997")
33
+ end
34
+
35
+ it "should return a Date of Sept 10, 1997 00:00 for a wkst of 3 - Wednesday" do
36
+ @date.start_of_week_with_wkst(3).should == Date.parse("Sept 10, 1997")
37
+ end
38
+
39
+ it "should return a Date of Sept 4, 1997 00:00 for a wkst of 4 - Thursday" do
40
+ @date.start_of_week_with_wkst(4).should == Date.parse("Sept 4, 1997")
41
+ end
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,573 @@
1
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
2
+
3
+ require File.join(File.dirname(__FILE__), %w[.. spec_helper.rb])
4
+
5
+ def mock_enumerator(name, next_occurrence)
6
+ mock(name, :next_occurrence => next_occurrence, :bounded? => true, :empty? => false)
7
+ end
8
+
9
+ # Note that this is more of a functional spec
10
+ describe RiCal::OccurrenceEnumerator do
11
+
12
+ Fr13Unbounded_Zulu = <<-TEXT
13
+ BEGIN:VEVENT
14
+ DTSTART:19970902T090000Z
15
+ EXDATE:19970902T090000Z
16
+ RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13
17
+ END:VEVENT
18
+ TEXT
19
+
20
+ Fr13Unbounded_Eastern = <<-TEXT
21
+ BEGIN:VEVENT
22
+ DTSTART;TZID=US-Eastern:19970902T090000
23
+ EXDATE;TZID=US-Eastern:19970902T090000
24
+ RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13
25
+ END:VEVENT
26
+ TEXT
27
+
28
+ Fr13UnboundedZuluExpectedFive = [
29
+ "19980213T090000Z",
30
+ "19980313T090000Z",
31
+ "19981113T090000Z",
32
+ "19990813T090000Z",
33
+ "20001013T090000Z"
34
+ ].map {|start| src = <<-TEXT
35
+ BEGIN:VEVENT
36
+ DTSTART:#{start}
37
+ RECURRENCE-ID:#{start}
38
+ END:VEVENT
39
+ TEXT
40
+ RiCal.parse_string(src).first
41
+ }
42
+
43
+ describe ".occurrences" do
44
+ describe "with an unbounded component" do
45
+ before(:each) do
46
+ @it = RiCal.parse_string(Fr13Unbounded_Zulu).first
47
+ end
48
+
49
+ it "should raise an ArgumentError with no options to limit result" do
50
+ lambda {@it.occurrences}.should raise_error(ArgumentError)
51
+ end
52
+
53
+ it "should have the right five occurrences when :count => 5 option is used" do
54
+ result = @it.occurrences(:count => 5)
55
+ result.should == Fr13UnboundedZuluExpectedFive
56
+ end
57
+
58
+ end
59
+ end
60
+
61
+ describe ".occurrences" do
62
+ before(:each) do
63
+ @it = RiCal.parse_string(Fr13Unbounded_Zulu).first
64
+ end
65
+
66
+ describe "with :starting specified" do
67
+ it "should exclude dates before :starting" do
68
+ result = @it.occurrences(:starting => Fr13UnboundedZuluExpectedFive[1].dtstart,
69
+ :before => Fr13UnboundedZuluExpectedFive[-1].dtstart)
70
+ result.map{|o|o.dtstart}.should == Fr13UnboundedZuluExpectedFive[1..-2].map{|e| e.dtstart}
71
+ end
72
+ end
73
+
74
+ describe "with :before specified" do
75
+ it "should exclude dates after :before" do
76
+ result = @it.occurrences(:before => Fr13UnboundedZuluExpectedFive[3].dtstart,
77
+ :count => 5)
78
+ result.map{|o|o.dtstart}.should == Fr13UnboundedZuluExpectedFive[0..2].map{|e| e.dtstart}
79
+ end
80
+ end
81
+ end
82
+
83
+
84
+ describe ".each" do
85
+ describe " for Every Friday the 13th, forever" do
86
+ before(:each) do
87
+ event = RiCal.parse_string(Fr13Unbounded_Zulu).first
88
+ @result = []
89
+ event.each do |occurrence|
90
+ break if @result.length >= 5
91
+ @result << occurrence
92
+ end
93
+ end
94
+
95
+ it "should have the right first six occurrences" do
96
+ # TODO - Need to properly deal with timezones
97
+ @result.should == Fr13UnboundedZuluExpectedFive
98
+ end
99
+
100
+ end
101
+ end
102
+ end
103
+
104
+ describe RiCal::OccurrenceEnumerator::OccurrenceMerger do
105
+ before(:each) do
106
+ @merger = RiCal::OccurrenceEnumerator::OccurrenceMerger
107
+ end
108
+
109
+ describe ".for" do
110
+ it "should return an EmptyEnumerator if the rules parameter is nil" do
111
+ @merger.for(nil, nil).should == RiCal::OccurrenceEnumerator::EmptyRulesEnumerator
112
+ end
113
+
114
+ it "should return an EmptyEnumerator if the rules parameter is empty" do
115
+ @merger.for(nil, []).should == RiCal::OccurrenceEnumerator::EmptyRulesEnumerator
116
+ end
117
+
118
+ describe "with a single rrule" do
119
+ before(:each) do
120
+ @component = mock("component", :dtstart => :dtstart_value)
121
+ @rrule = mock("rrule", :enumerator => :rrule_enumerator)
122
+ end
123
+
124
+ it "should return the enumerator the rrule" do
125
+ @merger.for(@component, [@rrule]).should == :rrule_enumerator
126
+ end
127
+
128
+ it "should pass the component to the enumerator instantiation" do
129
+ @rrule.should_receive(:enumerator).with(@component)
130
+ @merger.for(@component, [@rrule])
131
+ end
132
+ end
133
+
134
+ describe "with multiple rrules" do
135
+ before(:each) do
136
+ @component = mock("component", :dtstart => :dtstart_value)
137
+ @enum1 = mock_enumerator("rrule_enumerator1", :occ1)
138
+ @enum2 = mock_enumerator("rrule_enumerator2", :occ2)
139
+ @rrule1 = mock("rrule", :enumerator => @enum1)
140
+ @rrule2 = mock("rrule", :enumerator => @enum2)
141
+ end
142
+
143
+ it "should return an instance of RiCal::OccurrenceEnumerator::OccurrenceMerger" do
144
+ @merger.for(@component, [@rrule1, @rrule2]).should be_kind_of(RiCal::OccurrenceEnumerator::OccurrenceMerger)
145
+ end
146
+
147
+ it "should pass the component to the enumerator instantiation" do
148
+ @rrule1.should_receive(:enumerator).with(@component).and_return(@enum1)
149
+ @rrule2.should_receive(:enumerator).with(@component).and_return(@enum2)
150
+ @merger.for(@component, [@rrule1, @rrule2])
151
+ end
152
+
153
+ it "should preload the next occurrences" do
154
+ @enum1.should_receive(:next_occurrence).and_return(:occ1)
155
+ @enum2.should_receive(:next_occurrence).and_return(:occ2)
156
+ @merger.for(@component, [@rrule1, @rrule2])
157
+ end
158
+ end
159
+ end
160
+
161
+ describe "#zulu_occurrence_range" do
162
+ end
163
+
164
+ describe "#next_occurence" do
165
+
166
+ describe "with unique nexts" do
167
+ before(:each) do
168
+ @enum1 = mock_enumerator("rrule_enumerator1",3)
169
+ @enum2 = mock_enumerator("rrule_enumerator2", 2)
170
+ @rrule1 = mock("rrule", :enumerator => @enum1)
171
+ @rrule2 = mock("rrule", :enumerator => @enum2)
172
+ @it = @merger.new(0, [@rrule1, @rrule2])
173
+ end
174
+
175
+ it "should return the earliest occurrence" do
176
+ @it.next_occurrence.should == 2
177
+ end
178
+
179
+ it "should advance the enumerator which returned the result" do
180
+ @enum2.should_receive(:next_occurrence).and_return(4)
181
+ @it.next_occurrence
182
+ end
183
+
184
+ it "should not advance the other enumerator" do
185
+ @enum1.should_not_receive(:next_occurrence)
186
+ @it.next_occurrence
187
+ end
188
+
189
+ it "should properly update the next array" do
190
+ @enum2.stub!(:next_occurrence).and_return(4)
191
+ @it.next_occurrence
192
+ @it.nexts.should == [3, 4]
193
+ end
194
+ end
195
+
196
+ describe "with duplicated nexts" do
197
+ before(:each) do
198
+ @enum1 = mock_enumerator("rrule_enumerator1", 2)
199
+ @enum2 = mock_enumerator("rrule_enumerator2", 2)
200
+ @rrule1 = mock("rrule", :enumerator => @enum1)
201
+ @rrule2 = mock("rrule", :enumerator => @enum2)
202
+ @it = @merger.new(0, [@rrule1, @rrule2])
203
+ end
204
+
205
+ it "should return the earliest occurrence" do
206
+ @it.next_occurrence.should == 2
207
+ end
208
+
209
+ it "should advance both enumerators" do
210
+ @enum1.should_receive(:next_occurrence).and_return(5)
211
+ @enum2.should_receive(:next_occurrence).and_return(4)
212
+ @it.next_occurrence
213
+ end
214
+
215
+ it "should properly update the next array" do
216
+ @enum1.stub!(:next_occurrence).and_return(5)
217
+ @enum2.stub!(:next_occurrence).and_return(4)
218
+ @it.next_occurrence
219
+ @it.nexts.should == [5, 4]
220
+ end
221
+
222
+ end
223
+
224
+ describe "with all enumerators at end" do
225
+ before(:each) do
226
+ @enum1 = mock_enumerator("rrule_enumerator1", nil)
227
+ @enum2 = mock_enumerator("rrule_enumerator2", nil)
228
+ @rrule1 = mock("rrule", :enumerator => @enum1)
229
+ @rrule2 = mock("rrule", :enumerator => @enum2)
230
+ @it = @merger.new(0, [@rrule1, @rrule2])
231
+ end
232
+
233
+ it "should return nil" do
234
+ @it.next_occurrence.should == nil
235
+ end
236
+
237
+ it "should not advance the enumerators which returned the result" do
238
+ @enum1.should_not_receive(:next_occurrence)
239
+ @enum2.should_not_receive(:next_occurrence)
240
+ @it.next_occurrence
241
+ end
242
+ end
243
+ end
244
+
245
+ context "Ticket #4 from paulsm" do
246
+ it "should produce 4 occurrences" do
247
+ cal = RiCal.parse_string <<ENDCAL
248
+ BEGIN:VCALENDAR
249
+ METHOD:PUBLISH
250
+ PRODID:-//Apple Inc.//iCal 3.0//EN
251
+ CALSCALE:GREGORIAN
252
+ X-WR-CALNAME:Australian32Holidays
253
+ X-WR-CALDESC:Australian Public Holidays. Compiled from http://www.indust
254
+ rialrelations.nsw.gov.au/holidays/default.html and the links for the oth
255
+ er states at the bottom of that page
256
+ X-WR-RELCALID:AC1E4CE8-6690-49F6-A144-2F8891DFFD8D
257
+ VERSION:2.0
258
+ X-WR-TIMEZONE:Australia/Sydney
259
+ BEGIN:VEVENT
260
+ SEQUENCE:8
261
+ TRANSP:OPAQUE
262
+ UID:5B5579F3-2137-11D7-B491-00039301B0C2
263
+ DTSTART;VALUE=DATE:20020520
264
+ DTSTAMP:20060602T045619Z
265
+ SUMMARY:Adelaide Cup Carnival and Volunteers Day (SA)
266
+ EXDATE;VALUE=DATE:20060515
267
+ CREATED:20080916T000924Z
268
+ DTEND;VALUE=DATE:20020521
269
+ RRULE:FREQ=YEARLY;INTERVAL=1;UNTIL=20070520;BYMONTH=5;BYDAY=3MO
270
+ END:VEVENT
271
+ END:VCALENDAR
272
+ ENDCAL
273
+ cal.first.events.first.occurrences.length.should == 4
274
+ end
275
+ end
276
+
277
+ context "Ticket #2 from paulsm" do
278
+ before(:each) do
279
+ cals = RiCal.parse_string <<ENDCAL
280
+ BEGIN:VCALENDAR
281
+ X-WR-TIMEZONE:America/New_York
282
+ PRODID:-//Apple Inc.//iCal 3.0//EN
283
+ CALSCALE:GREGORIAN
284
+ X-WR-CALNAME:test
285
+ VERSION:2.0
286
+ X-WR-RELCALID:1884C7F8-BC8E-457F-94AC-297871967D5E
287
+ X-APPLE-CALENDAR-COLOR:#2CA10B
288
+ BEGIN:VTIMEZONE
289
+ TZID:US/Eastern
290
+ BEGIN:DAYLIGHT
291
+ TZOFFSETFROM:-0500
292
+ TZOFFSETTO:-0400
293
+ DTSTART:20070311T020000
294
+ RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
295
+ TZNAME:EDT
296
+ END:DAYLIGHT
297
+ BEGIN:STANDARD
298
+ TZOFFSETFROM:-0400
299
+ TZOFFSETTO:-0500
300
+ DTSTART:20071104T020000
301
+ RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
302
+ TZNAME:EST
303
+ END:STANDARD
304
+ END:VTIMEZONE
305
+ BEGIN:VEVENT
306
+ SEQUENCE:5
307
+ TRANSP:OPAQUE
308
+ UID:00481E53-9258-4EA7-9F8D-947D3041A3F2
309
+ DTSTART;TZID=US/Eastern:20090224T090000
310
+ DTSTAMP:20090225T000908Z
311
+ SUMMARY:Test Event
312
+ CREATED:20090225T000839Z
313
+ DTEND;TZID=US/Eastern:20090224T100000
314
+ RRULE:FREQ=DAILY;INTERVAL=1;UNTIL=20090228T045959Z
315
+ END:VEVENT
316
+ END:VCALENDAR
317
+ ENDCAL
318
+ @event = cals.first.events.first
319
+ end
320
+
321
+
322
+ it "the event should be enumerable" do
323
+ lambda {@event.occurrences}.should_not raise_error
324
+ end
325
+ end
326
+
327
+ context "Lighthouse bug #3" do
328
+ before(:each) do
329
+ cals = RiCal.parse_string <<ENDCAL
330
+ BEGIN:VCALENDAR
331
+ VERSION:2.0
332
+ PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
333
+ BEGIN:VTIMEZONE
334
+ TZID:/mozilla.org/20070129_1/Europe/Paris
335
+ X-LIC-LOCATION:Europe/Paris
336
+ BEGIN:DAYLIGHT
337
+ TZOFFSETFROM:+0100
338
+ TZOFFSETTO:+0200
339
+ TZNAME:CEST
340
+ DTSTART:19700329T020000
341
+ RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3
342
+ END:DAYLIGHT
343
+ BEGIN:STANDARD
344
+ TZOFFSETFROM:+0200
345
+ TZOFFSETTO:+0100
346
+ TZNAME:CET
347
+ DTSTART:19701025T030000
348
+ RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
349
+ END:STANDARD
350
+ END:VTIMEZONE
351
+ BEGIN:VEVENT
352
+ CREATED:20070606T141629Z
353
+ LAST-MODIFIED:20070606T154611Z
354
+ DTSTAMP:20070607T120859Z
355
+ UID:5d1ae55f-3910-4de9-8b65-d652768fb2f2
356
+ SUMMARY:Lundi de Pâques
357
+ DTSTART;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070409
358
+ DTEND;VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Paris:20070410
359
+ CATEGORIES:Jours fériés
360
+ END:VEVENT
361
+ END:VCALENDAR
362
+ ENDCAL
363
+ @event = cals.first.events.first
364
+ end
365
+
366
+ it "should be able to enumerate occurrences" do
367
+ @event.occurrences.should == [@event]
368
+ end
369
+ end
370
+
371
+ context "An event with a DATE dtstart, Ticket #6" do
372
+ before(:each) do
373
+ cal = RiCal.parse_string <<ENDCAL
374
+ BEGIN:VCALENDAR
375
+ PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN
376
+ VERSION:2.0
377
+ BEGIN:VEVENT
378
+ CREATED:20090520T092032Z
379
+ LAST-MODIFIED:20090520T092052Z
380
+ DTSTAMP:20090520T092032Z
381
+ UID:d41c124a-65c3-400e-bd04-1d2ee7b98352
382
+ SUMMARY:event2
383
+ RRULE:FREQ=MONTHLY;INTERVAL=1
384
+ DTSTART;VALUE=DATE:20090603
385
+ DTEND;VALUE=DATE:20090604
386
+ TRANSP:TRANSPARENT
387
+ END:VEVENT
388
+ END:VCALENDAR
389
+ ENDCAL
390
+ @occurrences = cal.first.events.first.occurrences(
391
+ :after => Date.parse('01/01/1990'),
392
+ :before => Date.parse("01/01/2010")
393
+ )
394
+ end
395
+
396
+ it "should produce the right dtstart values" do
397
+ @occurrences.map {|o| o.dtstart}.should == [
398
+ Date.parse("2009-06-03"),
399
+ Date.parse("2009-07-03"),
400
+ Date.parse("2009-08-03"),
401
+ Date.parse("2009-09-03"),
402
+ Date.parse("2009-10-03"),
403
+ Date.parse("2009-11-03"),
404
+ Date.parse("2009-12-03")
405
+ ]
406
+ end
407
+
408
+ it "should produce events whose dtstarts are all dates" do
409
+ @occurrences.all? {|o| o.dtstart.class == ::Date}.should be_true
410
+ end
411
+
412
+ it "should produce the right dtend values" do
413
+ @occurrences.map {|o| o.dtend}.should == [
414
+ Date.parse("2009-06-04"),
415
+ Date.parse("2009-07-04"),
416
+ Date.parse("2009-08-04"),
417
+ Date.parse("2009-09-04"),
418
+ Date.parse("2009-10-04"),
419
+ Date.parse("2009-11-04"),
420
+ Date.parse("2009-12-04")
421
+ ]
422
+ end
423
+
424
+ it "should produce events whose dtstends are all dates" do
425
+ @occurrences.all? {|o| o.dtend.class == ::Date}.should be_true
426
+ end
427
+ end
428
+ context "bounded? bug" do
429
+ before(:each) do
430
+ events = RiCal.parse_string rectify_ical <<-ENDCAL
431
+ BEGIN:VEVENT
432
+ EXDATE:20090114T163000
433
+ EXDATE:20090128T163000
434
+ EXDATE:20090121T163000
435
+ EXDATE:20090211T163000
436
+ EXDATE:20090204T163000
437
+ EXDATE:20090218T163000
438
+ TRANSP:OPAQUE
439
+ DTSTAMP;VALUE=DATE-TIME:20090107T024340Z
440
+ CREATED;VALUE=DATE-TIME:20090107T024012Z
441
+ DTEND;TZID=US/Mountain;VALUE=DATE-TIME:20090114T180000
442
+ DTSTART;TZID=US/Mountain;VALUE=DATE-TIME:20090114T163000
443
+ UID:15208112-E0FA-4A7C-954C-CFDF19D1B0E7
444
+ RRULE:FREQ=WEEKLY;INTERVAL=1;UNTIL=20090219T065959Z
445
+ SUMMARY:Wild Rose XC/Skate Training Series
446
+ SEQUENCE:11
447
+ LOCATION:Mountain Dell Golf Course
448
+ END:VEVENT
449
+ ENDCAL
450
+ @event = events.first
451
+ end
452
+
453
+ it "should be able to enumerate occurrences" do
454
+ @event.should be_bounded
455
+ end
456
+ end
457
+
458
+ context "EXDATES with timezones bug" do
459
+ before(:each) do
460
+ cals = RiCal.parse_string rectify_ical <<-ENDCAL
461
+ BEGIN:VCALENDAR
462
+ METHOD:PUBLISH
463
+ PRODID:-//Apple Inc.//iCal 3.0//EN
464
+ CALSCALE:GREGORIAN
465
+ X-WR-CALNAME:Utah Cycling
466
+ X-WR-RELCALID:BF579011-36BF-49C6-8C7D-E96F03DE8055
467
+ VERSION:2.0
468
+ X-WR-TIMEZONE:US/Mountain
469
+ BEGIN:VTIMEZONE
470
+ TZID:US/Mountain
471
+ BEGIN:DAYLIGHT
472
+ TZOFFSETFROM:-0700
473
+ TZOFFSETTO:-0600
474
+ DTSTART:20070311T020000
475
+ RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
476
+ TZNAME:MDT
477
+ END:DAYLIGHT
478
+ BEGIN:STANDARD
479
+ TZOFFSETFROM:-0600
480
+ TZOFFSETTO:-0700
481
+ DTSTART:20071104T020000
482
+ RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
483
+ TZNAME:MST
484
+ END:STANDARD
485
+ END:VTIMEZONE
486
+ BEGIN:VEVENT
487
+ SEQUENCE:11
488
+ TRANSP:OPAQUE
489
+ UID:15208112-E0FA-4A7C-954C-CFDF19D1B0E7
490
+ DTSTART;TZID=US/Mountain:20090114T163000
491
+ DTSTAMP:20090107T024340Z
492
+ SUMMARY:Wild Rose XC/Skate Training Series
493
+ EXDATE;TZID=US/Mountain:20090114T163000
494
+ EXDATE;TZID=US/Mountain:20090128T163000
495
+ EXDATE;TZID=US/Mountain:20090121T163000
496
+ EXDATE;TZID=US/Mountain:20090211T163000
497
+ EXDATE;TZID=US/Mountain:20090204T163000
498
+ EXDATE;TZID=US/Mountain:20090218T163000
499
+ CREATED:20090107T024012Z
500
+ DTEND;TZID=US/Mountain:20090114T180000
501
+ LOCATION:Mountain Dell Golf Course
502
+ RRULE:FREQ=WEEKLY;INTERVAL=1;UNTIL=20090219T065959Z
503
+ END:VEVENT
504
+ END:VCALENDAR
505
+ ENDCAL
506
+ @event = cals.first.events.first
507
+ end
508
+
509
+ it "should have no occurrences" do
510
+ @event.occurrences.length.should == 0
511
+ end
512
+ end
513
+
514
+
515
+ describe "#zulu_occurrence_range" do
516
+ context "For an unbounded recurring event" do
517
+ before(:each) do
518
+ @event = RiCal.Event do |e|
519
+ e.dtstart = "TZID=America/New_York:20090525T143500"
520
+ e.dtend = "TZID=America/New_York:20090525T153500"
521
+ e.add_rrule("FREQ=DAILY")
522
+ end
523
+
524
+ it "should return an array with the first dtstart and nil" do
525
+ @event.zulu_occurrence_range.should == [DateTime.civil(2009,5,25,18,35,00, 0), nil]
526
+ end
527
+ end
528
+ end
529
+
530
+ context "For a bounded recurring event" do
531
+ before(:each) do
532
+ @event = RiCal.Event do |e|
533
+ e.dtstart = "TZID=America/New_York:20090525T143500"
534
+ e.dtend = "TZID=America/New_York:20090525T153500"
535
+ e.add_rrule("FREQ=DAILY;COUNT=3")
536
+ end
537
+
538
+ it "should return an array with the first dtstart last dtend converted to utc" do
539
+ @event.zulu_occurrence_range.should == [DateTime.civil(2009,5,25,18,35,00, 0), DateTime.civil(2009,5,27,19,35,00, 0)]
540
+ end
541
+ end
542
+ end
543
+
544
+ context "For an event with no recurrence rules" do
545
+ context "with a non-floating dtstart and dtend" do
546
+ before(:each) do
547
+ @event = RiCal.Event do |e|
548
+ e.dtstart = "TZID=America/New_York:20090525T143500"
549
+ e.dtend = "TZID=America/New_York:20090525T153500"
550
+ end
551
+ end
552
+
553
+ it "should return an array with dtstart and dtend converted to zulu time" do
554
+ @event.zulu_occurrence_range.should == [DateTime.civil(2009,5,25,18,35,00, 0), DateTime.civil(2009,5,25,19,35,00, 0)]
555
+ end
556
+ end
557
+ context "with a floating dtstart and dtend" do
558
+ before(:each) do
559
+ @event = RiCal.Event do |e|
560
+ e.dtstart = "20090525T143500"
561
+ e.dtend = "20090525T153500"
562
+ end
563
+ end
564
+
565
+ it "should return an array with dtstart in the first timezone and dtend in the last time zone converted to zulu time" do
566
+ @event.zulu_occurrence_range.should == [DateTime.civil(2009,5,25,2,35,00, 0), DateTime.civil(2009,5,26,3,35,00, 0)]
567
+ end
568
+ end
569
+ end
570
+ end
571
+
572
+
573
+ end