ri_cal 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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