demingfactor-ri_cal 0.9.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 (167) hide show
  1. checksums.yaml +7 -0
  2. data/History.txt +402 -0
  3. data/Manifest.txt +161 -0
  4. data/README.txt +410 -0
  5. data/Rakefile +69 -0
  6. data/VERSION +1 -0
  7. data/bin/ri_cal +8 -0
  8. data/component_attributes/alarm.yml +10 -0
  9. data/component_attributes/calendar.yml +4 -0
  10. data/component_attributes/component_property_defs.yml +180 -0
  11. data/component_attributes/event.yml +45 -0
  12. data/component_attributes/freebusy.yml +16 -0
  13. data/component_attributes/journal.yml +35 -0
  14. data/component_attributes/timezone.yml +3 -0
  15. data/component_attributes/timezone_period.yml +11 -0
  16. data/component_attributes/todo.yml +46 -0
  17. data/copyrights.txt +1 -0
  18. data/docs/draft-ietf-calsify-2446bis-08.txt +7280 -0
  19. data/docs/draft-ietf-calsify-rfc2445bis-09.txt +10416 -0
  20. data/docs/incrementers.txt +7 -0
  21. data/docs/rfc2445.pdf +0 -0
  22. data/lib/ri_cal/component/alarm.rb +19 -0
  23. data/lib/ri_cal/component/calendar.rb +257 -0
  24. data/lib/ri_cal/component/event.rb +58 -0
  25. data/lib/ri_cal/component/freebusy.rb +16 -0
  26. data/lib/ri_cal/component/journal.rb +27 -0
  27. data/lib/ri_cal/component/non_standard.rb +33 -0
  28. data/lib/ri_cal/component/t_z_info_timezone.rb +153 -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 +76 -0
  32. data/lib/ri_cal/component/timezone.rb +197 -0
  33. data/lib/ri_cal/component/todo.rb +42 -0
  34. data/lib/ri_cal/component.rb +256 -0
  35. data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
  36. data/lib/ri_cal/core_extensions/array.rb +7 -0
  37. data/lib/ri_cal/core_extensions/date/conversions.rb +56 -0
  38. data/lib/ri_cal/core_extensions/date.rb +13 -0
  39. data/lib/ri_cal/core_extensions/date_time/conversions.rb +50 -0
  40. data/lib/ri_cal/core_extensions/date_time.rb +15 -0
  41. data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
  42. data/lib/ri_cal/core_extensions/object.rb +8 -0
  43. data/lib/ri_cal/core_extensions/string/conversions.rb +57 -0
  44. data/lib/ri_cal/core_extensions/string.rb +8 -0
  45. data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
  46. data/lib/ri_cal/core_extensions/time/conversions.rb +42 -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 +55 -0
  49. data/lib/ri_cal/core_extensions/time.rb +14 -0
  50. data/lib/ri_cal/core_extensions.rb +11 -0
  51. data/lib/ri_cal/fast_date_time.rb +234 -0
  52. data/lib/ri_cal/floating_timezone.rb +32 -0
  53. data/lib/ri_cal/invalid_property_value.rb +8 -0
  54. data/lib/ri_cal/invalid_timezone_identifier.rb +20 -0
  55. data/lib/ri_cal/occurrence_enumerator.rb +265 -0
  56. data/lib/ri_cal/occurrence_period.rb +17 -0
  57. data/lib/ri_cal/parser.rb +148 -0
  58. data/lib/ri_cal/properties/alarm.rb +390 -0
  59. data/lib/ri_cal/properties/calendar.rb +164 -0
  60. data/lib/ri_cal/properties/event.rb +1523 -0
  61. data/lib/ri_cal/properties/freebusy.rb +593 -0
  62. data/lib/ri_cal/properties/journal.rb +1237 -0
  63. data/lib/ri_cal/properties/timezone.rb +150 -0
  64. data/lib/ri_cal/properties/timezone_period.rb +416 -0
  65. data/lib/ri_cal/properties/todo.rb +1559 -0
  66. data/lib/ri_cal/properties.rb +12 -0
  67. data/lib/ri_cal/property_value/array.rb +27 -0
  68. data/lib/ri_cal/property_value/cal_address.rb +11 -0
  69. data/lib/ri_cal/property_value/date.rb +184 -0
  70. data/lib/ri_cal/property_value/date_time/additive_methods.rb +44 -0
  71. data/lib/ri_cal/property_value/date_time/time_machine.rb +159 -0
  72. data/lib/ri_cal/property_value/date_time/timezone_support.rb +100 -0
  73. data/lib/ri_cal/property_value/date_time.rb +359 -0
  74. data/lib/ri_cal/property_value/duration.rb +110 -0
  75. data/lib/ri_cal/property_value/geo.rb +11 -0
  76. data/lib/ri_cal/property_value/integer.rb +12 -0
  77. data/lib/ri_cal/property_value/occurrence_list.rb +144 -0
  78. data/lib/ri_cal/property_value/period.rb +86 -0
  79. data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +100 -0
  80. data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +79 -0
  81. data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +148 -0
  82. data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +53 -0
  83. data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +31 -0
  84. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_day_incrementer.rb +86 -0
  85. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_hour_incrementer.rb +31 -0
  86. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_minute_incrementer.rb +32 -0
  87. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_month_incrementer.rb +52 -0
  88. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_monthday_incrementer.rb +31 -0
  89. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_numbered_day_incrementer.rb +38 -0
  90. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_second_incrementer.rb +32 -0
  91. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_weekno_incrementer.rb +69 -0
  92. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_yearday_incrementer.rb +31 -0
  93. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/daily_incrementer.rb +28 -0
  94. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/frequency_incrementer.rb +80 -0
  95. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/hourly_incrementer.rb +23 -0
  96. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/list_incrementer.rb +106 -0
  97. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/minutely_incrementer.rb +23 -0
  98. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/monthly_incrementer.rb +33 -0
  99. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/null_sub_cycle_incrementer.rb +43 -0
  100. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/secondly_incrementer.rb +28 -0
  101. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/weekly_incrementer.rb +37 -0
  102. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/yearly_incrementer.rb +57 -0
  103. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer.rb +135 -0
  104. data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +131 -0
  105. data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +64 -0
  106. data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +33 -0
  107. data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +53 -0
  108. data/lib/ri_cal/property_value/recurrence_rule/time_manipulation.rb +42 -0
  109. data/lib/ri_cal/property_value/recurrence_rule/validations.rb +125 -0
  110. data/lib/ri_cal/property_value/recurrence_rule.rb +154 -0
  111. data/lib/ri_cal/property_value/text.rb +44 -0
  112. data/lib/ri_cal/property_value/uri.rb +11 -0
  113. data/lib/ri_cal/property_value/utc_offset.rb +33 -0
  114. data/lib/ri_cal/property_value/zulu_date_time.rb +34 -0
  115. data/lib/ri_cal/property_value.rb +159 -0
  116. data/lib/ri_cal/required_timezones.rb +55 -0
  117. data/lib/ri_cal.rb +187 -0
  118. data/parked_specs/ri_cal/claudio_a_bug_spec.rb +100 -0
  119. data/performance/empty_propval/subject.rb +43 -0
  120. data/performance/paris_eastern/subject.rb +90 -0
  121. data/performance/penultimate_weekday/subject.rb +15 -0
  122. data/performance/psm_big_enum/ical.ics +3171 -0
  123. data/performance/psm_big_enum/subject.rb +16 -0
  124. data/performance/utah_cycling/subject.rb +55 -0
  125. data/ri_cal.gemspec +244 -0
  126. data/script/benchmark_subject +23 -0
  127. data/script/console +10 -0
  128. data/script/destroy +14 -0
  129. data/script/generate +14 -0
  130. data/script/profile_subject +29 -0
  131. data/script/txt2html +71 -0
  132. data/spec/ri_cal/bugreports_spec.rb +276 -0
  133. data/spec/ri_cal/component/alarm_spec.rb +12 -0
  134. data/spec/ri_cal/component/calendar_spec.rb +88 -0
  135. data/spec/ri_cal/component/event_spec.rb +735 -0
  136. data/spec/ri_cal/component/freebusy_spec.rb +12 -0
  137. data/spec/ri_cal/component/journal_spec.rb +37 -0
  138. data/spec/ri_cal/component/t_z_info_timezone_spec.rb +60 -0
  139. data/spec/ri_cal/component/timezone_spec.rb +236 -0
  140. data/spec/ri_cal/component/todo_spec.rb +112 -0
  141. data/spec/ri_cal/component_spec.rb +224 -0
  142. data/spec/ri_cal/core_extensions/string/conversions_spec.rb +78 -0
  143. data/spec/ri_cal/core_extensions/time/calculations_spec.rb +188 -0
  144. data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +45 -0
  145. data/spec/ri_cal/fast_date_time_spec.rb +77 -0
  146. data/spec/ri_cal/inf_loop_spec.rb +78 -0
  147. data/spec/ri_cal/occurrence_enumerator_spec.rb +611 -0
  148. data/spec/ri_cal/parser_spec.rb +337 -0
  149. data/spec/ri_cal/property_value/date_spec.rb +53 -0
  150. data/spec/ri_cal/property_value/date_time_spec.rb +383 -0
  151. data/spec/ri_cal/property_value/duration_spec.rb +126 -0
  152. data/spec/ri_cal/property_value/occurrence_list_spec.rb +72 -0
  153. data/spec/ri_cal/property_value/period_spec.rb +63 -0
  154. data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +21 -0
  155. data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1814 -0
  156. data/spec/ri_cal/property_value/text_spec.rb +25 -0
  157. data/spec/ri_cal/property_value/utc_offset_spec.rb +48 -0
  158. data/spec/ri_cal/property_value_spec.rb +125 -0
  159. data/spec/ri_cal/required_timezones_spec.rb +67 -0
  160. data/spec/ri_cal_spec.rb +53 -0
  161. data/spec/spec.opts +4 -0
  162. data/spec/spec_helper.rb +50 -0
  163. data/tasks/gem_loader/load_active_support.rb +3 -0
  164. data/tasks/gem_loader/load_tzinfo_gem.rb +2 -0
  165. data/tasks/ri_cal.rake +412 -0
  166. data/tasks/spec.rake +102 -0
  167. metadata +246 -0
@@ -0,0 +1,735 @@
1
+ # encoding: utf-8
2
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
3
+
4
+ require File.join(File.dirname(__FILE__), %w[.. .. spec_helper])
5
+
6
+ describe RiCal::Component::Event do
7
+
8
+ context "with change management properties" do
9
+ it "should use zulu time for all change management datetime properties" do
10
+ new_york_offset = Rational(-1, 6)
11
+ cal = RiCal.Calendar
12
+ event = RiCal::Component::Event.new(cal)
13
+ event.dtstamp = result_time_in_zone(2010, 4, 1, 9, 23, 45, "America/New_York", new_york_offset)
14
+ event.created = result_time_in_zone(2010, 4, 1, 9, 23, 45, "America/New_York", new_york_offset)
15
+ event.last_modified = result_time_in_zone(2010, 4, 1, 12, 23, 45, "America/New_York", new_york_offset)
16
+ event.to_s.should == "BEGIN:VEVENT
17
+ CREATED;VALUE=DATE-TIME:20100401T132345Z
18
+ DTSTAMP;VALUE=DATE-TIME:20100401T132345Z
19
+ LAST-MODIFIED;VALUE=DATE-TIME:20100401T162345Z
20
+ END:VEVENT
21
+ "
22
+ end
23
+ end
24
+
25
+ context ".finish_time" do
26
+ it "should be the end of the start day for an event with a date dtstart and no dtend or duration" do
27
+ @it = RiCal.Event do |evt|
28
+ evt.dtstart = "20090704"
29
+ end
30
+ @it.finish_time.should == DateTime.parse("20090704T235959")
31
+ end
32
+
33
+ it "should be the end of the end day for an event with a date dtstart and a dtend" do
34
+ @it = RiCal.Event do |evt|
35
+ evt.dtstart = "20090704"
36
+ evt.dtend = "20090706"
37
+ end
38
+ @it.finish_time.should == DateTime.parse("20090706T235959")
39
+ end
40
+
41
+ it "should be the start time for an event with a datetime dtstart and no dtend or duration" do
42
+ @it = RiCal.Event do |evt|
43
+ evt.dtstart = "20090704T013000Z"
44
+ end
45
+ @it.finish_time.should == DateTime.parse("20090704T013000Z")
46
+ end
47
+
48
+ it "should be the end time for an event with a datetime dtend" do
49
+ @it = RiCal.Event do |evt|
50
+ evt.dtstart = "20090704"
51
+ evt.dtend = "20090706T120000"
52
+ end
53
+ @it.finish_time.should == DateTime.parse("20090706T120000")
54
+ end
55
+
56
+ it "should be the end time for an event with a datetime dtstart and a duration" do
57
+ @it = RiCal.Event do |evt|
58
+ evt.dtstart = "20090704T120000Z"
59
+ evt.duration = "PT1H30M"
60
+ end
61
+ @it.finish_time.should == DateTime.parse("20090704T133000Z")
62
+ end
63
+
64
+ it "should uset the timezone of dtstart when event has a duration" do
65
+ @it = RiCal.Event do |evt|
66
+ evt.dtstart = "TZID=Australia/Sydney:20090712T200000"
67
+ evt.duration = "PT1H"
68
+ end
69
+ @it.finish_time.should == DateTime.parse("2009-07-12T21:00:00+10:00")
70
+ end
71
+ end
72
+
73
+ context ".before_range?" do
74
+ context "with a Date dtstart and no dtend" do
75
+ before(:each) do
76
+ @it = RiCal.Event do |evt|
77
+ evt.dtstart = "20090704"
78
+ end
79
+ end
80
+
81
+ it "should be false if the range start is a date before the start date" do
82
+ @it.before_range?([Date.parse("20090703"), :anything]).should_not be
83
+ end
84
+
85
+ it "should be false if the range start is the start date" do
86
+ @it.before_range?([Date.parse("20090704"), :anything]).should_not be
87
+ end
88
+
89
+ it "should be true if the range start is a date after the start date" do
90
+ @it.before_range?([Date.parse("20090705"), :anything]).should be
91
+ end
92
+ end
93
+
94
+ context "with a Date dtstart and date dtend" do
95
+ before(:each) do
96
+ @it = RiCal.Event do |evt|
97
+ evt.dtstart = "20090704"
98
+ evt.dtend = "20090706"
99
+ end
100
+ end
101
+
102
+ it "should be false if the range start is a date before the end date" do
103
+ @it.before_range?([Date.parse("20090705"), :anything]).should_not be
104
+ end
105
+
106
+ it "should be false if the range start is the end date" do
107
+ @it.before_range?([Date.parse("20090706"), :anything]).should_not be
108
+ end
109
+
110
+ it "should be true if the range start is a date after the end date" do
111
+ @it.before_range?([Date.parse("20090707"), :anything]).should be
112
+ end
113
+ end
114
+ end
115
+
116
+ context "bug report from Noboyuki Tomizawa" do
117
+ before(:each) do
118
+
119
+ @it = RiCal.Calendar do |cal|
120
+ cal.event do |event|
121
+ event.description = "test"
122
+ event.dtstart = "TZID=Asia/Tokyo:20090530T123000"
123
+ event.dtend = "TZID=Asia/Tokyo:20090530T123001"
124
+ end
125
+ end
126
+ end
127
+
128
+ it "should not fail" do
129
+ lambda {@it.export}.should_not raise_error
130
+ end
131
+ end
132
+
133
+ context "rdate property methods" do
134
+ before(:each) do
135
+ @event = RiCal.Event do
136
+ rdate "20090101"
137
+ end
138
+ end
139
+
140
+ context "#rdate=" do
141
+
142
+ it "should accept a single Date and replace the existing rdate" do
143
+ @event.rdate = Date.parse("20090102")
144
+ @event.rdate.should == [[Date.parse("20090102")]]
145
+ end
146
+
147
+ it "should accept a single rfc2445 date format string and replace the existing rdate" do
148
+ @event.rdate = "20090102"
149
+ @event.rdate.should == [[Date.parse("20090102")]]
150
+ end
151
+
152
+ it "should accept a single DateTime and replace the existing rdate" do
153
+ @event.rdate = DateTime.parse("20090102T012345")
154
+ @event.rdate.should == [[DateTime.parse("20090102T012345")]]
155
+ end
156
+
157
+ it "should accept a single Time and replace the existing rdate" do
158
+ ::RiCal::PropertyValue::DateTime.default_tzid = 'UTC'
159
+ @event.rdate = Time.local(2009, 1, 2, 1, 23, 45)
160
+ @event.rdate.should == [[result_time_in_zone(2009, 1, 2, 1, 23, 45, "UTC")]]
161
+ end
162
+
163
+ it "should accept a single rfc2445 date-time format string and replace the existing rdate" do
164
+ @event.rdate = "20090102T012345"
165
+ @event.rdate.should == [[DateTime.parse("20090102T012345")]]
166
+ end
167
+
168
+ it "should accept a tzid prefixed rfc2445 date-time format string and replace the existing rdate" do
169
+ @event.rdate = "TZID=America/New_York:20090102T012345"
170
+ @event.rdate.should == [[result_time_in_zone(2009, 1, 2, 1, 23, 45, "America/New_York")]]
171
+ end
172
+
173
+ end
174
+
175
+ end
176
+
177
+ context "comment property methods" do
178
+ before(:each) do
179
+ @event = RiCal.Event
180
+ @event.comment = "Comment"
181
+ end
182
+
183
+ context "#comment=" do
184
+ it "should result in a single comment for the event" do
185
+ @event.comment.should == ["Comment"]
186
+ end
187
+
188
+ it "should replace existing comments" do
189
+ @event.comment = "Replacement"
190
+ @event.comment.should == ["Replacement"]
191
+ end
192
+ end
193
+
194
+ context "#comments=" do
195
+ it "should result in a multiple comments for the event replacing existing comments" do
196
+ @event.comments = "New1", "New2"
197
+ @event.comment.should == ["New1", "New2"]
198
+ end
199
+ end
200
+
201
+ context "#add_comment" do
202
+ it "should add a single comment" do
203
+ @event.add_comment "New1"
204
+ @event.comment.should == ["Comment", "New1"]
205
+ end
206
+ end
207
+
208
+ context "#add_comments" do
209
+ it "should add multiple comments" do
210
+ @event.add_comments "New1", "New2"
211
+ @event.comment.should == ["Comment", "New1", "New2"]
212
+ end
213
+ end
214
+
215
+ context "#remove_comment" do
216
+ it "should remove a single comment" do
217
+ @event.add_comment "New1"
218
+ @event.remove_comment "Comment"
219
+ @event.comment.should == ["New1"]
220
+ end
221
+ end
222
+
223
+ context "#remove_comments" do
224
+ it "should remove multiple comments" do
225
+ @event.add_comments "New1", "New2", "New3"
226
+ @event.remove_comments "New2", "Comment"
227
+ @event.comment.should == ["New1", "New3"]
228
+ end
229
+ end
230
+ end
231
+
232
+ context ".dtstart=" do
233
+ before(:each) do
234
+ @event = RiCal.Event
235
+ end
236
+
237
+ context "with a datetime only string" do
238
+ before(:each) do
239
+ @event.dtstart = "20090514T202400"
240
+ @it = @event.dtstart
241
+ end
242
+
243
+ it "should interpret it as the correct date-time" do
244
+ @it.should == DateTime.civil(2009, 5, 14, 20, 24, 00, Rational(0,24))
245
+ end
246
+
247
+ it "should interpret it as a floating date" do
248
+ @it.tzid.should == :floating
249
+ end
250
+ end
251
+
252
+ context "with a TZID and datetime string" do
253
+ before(:each) do
254
+ @event.dtstart = "TZID=America/New_York:20090514T202400"
255
+ @it = @event.dtstart
256
+ end
257
+
258
+ it "should interpret it as the correct date-time" do
259
+ @it.should == result_time_in_zone(2009, 5, 14, 20, 24, 00, "America/New_York")
260
+ end
261
+
262
+ it "should set the tzid to America/New_York" do
263
+ @it.tzid.should == "America/New_York"
264
+ end
265
+ end
266
+
267
+ context "with a zulu datetime only string" do
268
+ before(:each) do
269
+ @event.dtstart = "20090514T202400Z"
270
+ @it = @event.dtstart
271
+ end
272
+
273
+ it "should interpret it as the correct date-time" do
274
+ @it.should == DateTime.civil(2009, 5, 14, 20, 24, 00, Rational(0,24))
275
+ end
276
+
277
+ it "should set the tzid to UTC" do
278
+ @it.tzid.should == "UTC"
279
+ end
280
+ end
281
+
282
+ context "with a date string" do
283
+ before(:each) do
284
+ @event.dtstart = "20090514"
285
+ @it = @event.dtstart
286
+ end
287
+
288
+ it "should interpret it as the correct date-time" do
289
+ @it.should == Date.parse("14 May 2009")
290
+ end
291
+ end
292
+ end
293
+
294
+ context ".entity_name" do
295
+ it "should be VEVENT" do
296
+ RiCal::Component::Event.entity_name.should == "VEVENT"
297
+ end
298
+ end
299
+
300
+ context "with an rrule" do
301
+ before(:each) do
302
+ @it = RiCal::Component::Event.parse_string("BEGIN:VEVENT\nRRULE:FREQ=DAILY\nEND:VEVENT").first
303
+ end
304
+
305
+ it "should have an array of rrules" do
306
+ @it.rrule.should be_kind_of(Array)
307
+ end
308
+ end
309
+
310
+ context ".start_time" do
311
+
312
+ it "should be nil if there is no dtstart property" do
313
+ RiCal.Event.start_time.should be_nil
314
+ end
315
+
316
+ it "should be the same as dtstart for a date time" do
317
+ event = RiCal.Event {|e| e.dtstart = "20090525T151900"}
318
+ event.start_time.should == DateTime.civil(2009,05,25,15,19,0,0)
319
+ end
320
+
321
+ it "should be the start of the day of dtstart for a date" do
322
+ event = RiCal.Event {|e| e.dtstart = "20090525"}
323
+ event.start_time.should == DateTime.civil(2009,05,25,0,0,0,0)
324
+ end
325
+ end
326
+
327
+ context ".finish_time" do
328
+ before(:each) do
329
+ @event = RiCal.Event {|e| e.dtstart = "20090525T151900"}
330
+ end
331
+
332
+ context "with a given dtend" do
333
+ it "should be the same as dtend for a date time" do
334
+ @event.dtend = "20090525T161900"
335
+ @event.finish_time.should == DateTime.civil(2009,05,25,16,19,0,0)
336
+ end
337
+
338
+
339
+ end
340
+
341
+ context "with no dtend" do
342
+ context "and a duration" do
343
+ it "should be the dtstart plus the duration" do
344
+ @event.duration = "+PT1H"
345
+ @event.finish_time.should == DateTime.civil(2009,5,25,16,19,0,0)
346
+ end
347
+ end
348
+
349
+ context "and no duration" do
350
+ context "when the dtstart is not set" do
351
+ before(:each) do
352
+ @event.dtstart_property = nil
353
+ end
354
+
355
+ it "should be nil" do
356
+ @event.finish_time.should be_nil
357
+ end
358
+ end
359
+ context "when the dstart is a datetime" do
360
+ # For cases where a "VEVENT" calendar component
361
+ # specifies a "DTSTART" property with a DATE-TIME data type but no
362
+ # "DTEND" property, the event ends on the same calendar date and time
363
+ # of day specified by the "DTSTART" property. RFC 2445 p 53
364
+ it "should be the same as start_time" do
365
+ @event.finish_time.should == @event.start_time
366
+ end
367
+ end
368
+ context "when the dtstart is a date" do
369
+ # For cases where a "VEVENT" calendar component specifies a "DTSTART" property with a DATE data type
370
+ # but no "DTEND" property, the events non-inclusive end is the end of the calendar date specified by
371
+ # the "DTSTART" property. RFC 2445 p 53
372
+
373
+ it "should be the end of the same day as start_time" do
374
+ @event.dtstart = "20090525"
375
+ @event.finish_time.should == DateTime.civil(2009,5,25,23,59,59,0)
376
+ end
377
+ end
378
+ end
379
+ end
380
+
381
+ end
382
+
383
+ context ".zulu_occurrence_range_start_time" do
384
+
385
+ it "should be nil if there is no dtstart property" do
386
+ RiCal.Event.zulu_occurrence_range_start_time.should be_nil
387
+ end
388
+
389
+ it "should be the utc equivalent of dtstart for a date time" do
390
+ event = RiCal.Event {|e| e.dtstart = "TZID=America/New_York:20090525T151900"}
391
+ event.zulu_occurrence_range_start_time.should == DateTime.civil(2009,05,25,19,19,0,0)
392
+ end
393
+
394
+ it "should be the utc time of the start of the day of dtstart in the earliest timezone for a date" do
395
+ event = RiCal.Event {|e| e.dtstart = "20090525"}
396
+ result = event.zulu_occurrence_range_start_time
397
+ result.should == DateTime.civil(2009,05,24,12,0,0,0)
398
+ end
399
+
400
+ it "should be the utc time of the dtstart in the earliest timezone if dtstart is a floating datetime" do
401
+ event = RiCal.Event {|e| e.dtstart = "20090525T151900"}
402
+ event.zulu_occurrence_range_start_time.should == DateTime.civil(2009,05,25,3,19,0,0)
403
+ end
404
+ end
405
+
406
+ context ".zulu_occurrence_range_finish_time" do
407
+ before(:each) do
408
+ @event = RiCal.Event {|e| e.dtstart = "TZID=America/New_York:20090525T151900"}
409
+ end
410
+
411
+ context "with a given dtend" do
412
+ it "should be the utc equivalent of dtend if dtend is a date time" do
413
+ @event.dtend = "TZID=America/New_York:20090525T161900"
414
+ @event.zulu_occurrence_range_finish_time.should == DateTime.civil(2009,05,25, 20,19,0,0)
415
+ end
416
+ end
417
+
418
+ context "with no dtend" do
419
+ context "and a duration" do
420
+ it "should be the dtstart plus the duration" do
421
+ @event.duration = "+PT1H"
422
+ @event.zulu_occurrence_range_finish_time.should == DateTime.civil(2009,5,25,20 ,19,0,0)
423
+ end
424
+ end
425
+
426
+ context "and no duration" do
427
+ context "when the dtstart is not set" do
428
+ before(:each) do
429
+ @event.dtstart_property = nil
430
+ end
431
+
432
+ it "should be nil" do
433
+ @event.zulu_occurrence_range_finish_time.should be_nil
434
+ end
435
+ end
436
+
437
+ context "when the dstart is a datetime" do
438
+
439
+ it "should be the same as start_time" do
440
+ @event.zulu_occurrence_range_finish_time.should == @event.zulu_occurrence_range_start_time
441
+ end
442
+ end
443
+
444
+ context "when the dtstart is a date" do
445
+ it "should be the utc of end of the same day as start_time in the westermost time zone" do
446
+ @event.dtstart = "20090525"
447
+ @event.zulu_occurrence_range_finish_time.should == DateTime.civil(2009,5,26,11,59,59,0)
448
+ end
449
+ end
450
+ end
451
+ end
452
+ end
453
+
454
+ context "description property" do
455
+ before(:each) do
456
+ @ical_desc = "posted by Joyce per Zan\\nASheville\\, Rayan's Restauratn\\, Biltm\n ore Square"
457
+ @ruby_desc = "posted by Joyce per Zan\nASheville, Rayan's Restauratn, Biltmore Square"
458
+ @it = RiCal::Component::Event.parse_string("BEGIN:VEVENT\nDESCRIPTION:#{@ical_desc}\nEND:VEVENT").first
459
+ end
460
+
461
+ it "should product the converted ruby value" do
462
+ @it.description.should == @ruby_desc
463
+ end
464
+
465
+ it "should produce escaped text for ical" do
466
+ @it.description = "This is a\nnew description, yes; it is"
467
+ @it.description_property.value.should == 'This is a\nnew description\, yes\; it is'
468
+ end
469
+
470
+ end
471
+
472
+ context "with both dtend and duration specified" do
473
+ before(:each) do
474
+ @it = RiCal::Component::Event.parse_string("BEGIN:VEVENT\nDTEND:19970903T190000Z\nDURATION:H1\nEND:VEVENT").first
475
+ end
476
+
477
+ it "should be invalid" do
478
+ @it.should_not be_valid
479
+ end
480
+ end
481
+
482
+ context "with a duration property" do
483
+ before(:each) do
484
+ @it = RiCal::Component::Event.parse_string("BEGIN:VEVENT\nDURATION:H1\nEND:VEVENT").first
485
+ end
486
+
487
+ it "should have a duration property" do
488
+ @it.duration_property.should be
489
+ end
490
+
491
+ it "should have a duration of 1 Hour" do
492
+ @it.duration_property.value.should == "H1"
493
+ end
494
+
495
+ it "should reset the duration property if the dtend property is set" do
496
+ @it.dtend_property = "19970101T123456".to_ri_cal_date_time_value
497
+ @it.duration_property.should be_nil
498
+ end
499
+
500
+ it "should reset the duration property if the dtend ruby value is set" do
501
+ @it.dtend = "19970101"
502
+ @it.duration_property.should == nil
503
+ end
504
+ end
505
+
506
+ context "with a dtend property" do
507
+ before(:each) do
508
+ @it = RiCal::Component::Event.parse_string("BEGIN:VEVENT\nDTEND:19970903T190000Z\nEND:VEVENT").first
509
+ end
510
+
511
+ it "should have a duration property" do
512
+ @it.dtend_property.should be
513
+ end
514
+
515
+ it "should reset the dtend property if the duration property is set" do
516
+ @it.duration_property = "PT1H".to_ri_cal_duration_value
517
+ @it.dtend_property.should be_nil
518
+ end
519
+
520
+ it "should reset the dtend property if the duration ruby value is set" do
521
+ @it.duration = "PT1H".to_ri_cal_duration_value
522
+ @it.dtend_property.should be_nil
523
+ end
524
+ end
525
+
526
+ context "with a nested alarm component" do
527
+ before(:each) do
528
+ @it = RiCal::Component::Event.parse_string("BEGIN:VEVENT\nDTEND:19970903T190000Z\n\nBEGIN:VALARM\nEND:VALARM\nEND:VEVENT").first
529
+ end
530
+
531
+ it "should have one alarm" do
532
+ @it.alarms.length.should == 1
533
+ end
534
+
535
+ it "which should be an Alarm component" do
536
+ @it.alarms.first.should be_kind_of(RiCal::Component::Alarm)
537
+ end
538
+ end
539
+
540
+ context ".export" do
541
+ require 'rubygems'
542
+ require 'tzinfo'
543
+
544
+ def date_time_with_tzinfo_zone(date_time, timezone="America/New_York")
545
+ date_time.dup.set_tzid(timezone)
546
+ end
547
+
548
+ # Undo the effects of RFC2445 line folding
549
+ def unfold(string)
550
+ string.gsub("\n ", "")
551
+ end
552
+
553
+ before(:each) do
554
+ cal = RiCal.Calendar
555
+ @it = RiCal::Component::Event.new(cal)
556
+ end
557
+
558
+ it "should cause a VTIMEZONE to be included for a dtstart with a local timezone" do
559
+ @it.dtstart = date_time_with_tzinfo_zone(DateTime.parse("April 22, 2009 17:55"), "America/New_York")
560
+ unfold(@it.export).should match(/BEGIN:VTIMEZONE\nTZID;X-RICAL-TZSOURCE=TZINFO:America\/New_York\n/)
561
+ end
562
+
563
+ it "should properly format dtstart with a UTC date-time" do
564
+ @it.dtstart = DateTime.parse("April 22, 2009 1:23:45").set_tzid("UTC")
565
+ unfold(@it.export).should match(/^DTSTART;VALUE=DATE-TIME:20090422T012345Z$/)
566
+ end
567
+
568
+ it "should properly format dtstart with a floating date-time" do
569
+ @it.dtstart = DateTime.parse("April 22, 2009 1:23:45").with_floating_timezone
570
+ unfold(@it.export).should match(/^DTSTART;VALUE=DATE-TIME:20090422T012345$/)
571
+ end
572
+
573
+ it "should properly format dtstart with a local time zone" do
574
+ @it.dtstart = date_time_with_tzinfo_zone(DateTime.parse("April 22, 2009 17:55"), "America/New_York")
575
+ unfold(@it.export).should match(/^DTSTART;TZID=America\/New_York;VALUE=DATE-TIME:20090422T175500$/)
576
+ end
577
+
578
+ it "should properly format dtstart with a date" do
579
+ @it.dtstart = Date.parse("April 22, 2009")
580
+ unfold(@it.export).should match(/^DTSTART;VALUE=DATE:20090422$/)
581
+ end
582
+
583
+ it "should properly fold on export when the description contains a carriage return" do
584
+ @it.description = "Weather report looks nice, 80 degrees and partly cloudy, so following Michael's suggestion, let's meet at the food court at Crossroads:\n\rhttp://www.shopcrossroadsplaza.c...\n"
585
+ export_string = @it.export
586
+ export_string.should match(%r(^DESCRIPTION:Weather report looks nice\\, 80 degrees and partly cloudy\\, so$))
587
+ export_string.should match(%r(^ following Michael's suggestion\\, let's meet at the food court at Crossr$))
588
+ export_string.should match(%r(^ oads:\\nhttp://www\.shopcrossroadsplaza.c\.\.\.\\n$))
589
+ end
590
+
591
+ it "should properly fold on export when the description contains multi-byte UTF-8 Characters" do
592
+ @it.description = "Juin 2009 <<Alliance Francaise Reunion>> lieu Café périferôl"
593
+ export_string = @it.export
594
+ export_string.should match(%r(^DESCRIPTION:Juin 2009 <<Alliance Francaise Reunion>> lieu Café périfer$))
595
+ export_string.should match(%r(^ ôl$))
596
+ end
597
+ end
598
+
599
+ if RiCal::TimeWithZone
600
+ context "with ActiveSupport loaded" do
601
+
602
+ context "an event with an timezoned exdate" do
603
+ before(:each) do
604
+ @old_timezone = Time.zone
605
+ Time.zone = "America/New_York"
606
+ @exception_date_time = Time.zone.local(2009, 5, 19, 11, 13)
607
+ cal = RiCal.Calendar do |cal|
608
+ cal.event do |event|
609
+ event.add_exdate @exception_date_time
610
+ end
611
+ end
612
+ @event = cal.events.first
613
+ end
614
+
615
+ after(:each) do
616
+ Time.zone = @old_timezone
617
+ end
618
+
619
+ it "should pickup the timezone in the exdate property" do
620
+ @event.exdate.first.first.tzid.should == "America/New_York"
621
+ end
622
+
623
+ it "should have the timezone in the ical representation of the exdate property" do
624
+ @event.exdate_property.first.to_s.should match(%r{;TZID=America/New_York[:;]})
625
+ end
626
+ end
627
+
628
+ context "An event in a non-tzinfo source calendar" do
629
+ before(:each) do
630
+ cals = RiCal.parse_string <<ENDCAL
631
+ BEGIN:VCALENDAR
632
+ X-WR-TIMEZONE:America/New_York
633
+ PRODID:-//Apple Inc.//iCal 3.0//EN
634
+ CALSCALE:GREGORIAN
635
+ BEGIN:VTIMEZONE
636
+ TZID:US/Eastern
637
+ BEGIN:DAYLIGHT
638
+ TZOFFSETFROM:-0500
639
+ TZOFFSETTO:-0400
640
+ DTSTART:20070311T020000
641
+ RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
642
+ TZNAME:EDT
643
+ END:DAYLIGHT
644
+ BEGIN:STANDARD
645
+ TZOFFSETFROM:-0400
646
+ TZOFFSETTO:-0500
647
+ DTSTART:20071104T020000
648
+ RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
649
+ TZNAME:EST
650
+ END:STANDARD
651
+ END:VTIMEZONE
652
+ BEGIN:VEVENT
653
+ SEQUENCE:5
654
+ TRANSP:OPAQUE
655
+ UID:00481E53-9258-4EA7-9F8D-947D3041A3F2
656
+ DTSTART;TZID=US/Eastern:20090224T090000
657
+ DTSTAMP:20090225T000908Z
658
+ SUMMARY:Test Event
659
+ CREATED:20090225T000839Z
660
+ DTEND;TZID=US/Eastern:20090224T100000
661
+ RRULE:FREQ=DAILY;INTERVAL=1;UNTIL=20090228T045959Z
662
+ END:VEVENT
663
+ END:VCALENDAR
664
+ ENDCAL
665
+ @event = cals.first.events.first
666
+ end
667
+
668
+ it "should produce a DateTime for dtstart" do
669
+ @event.dtstart.should be_instance_of(DateTime)
670
+ end
671
+ end
672
+
673
+ context "An event starting in Paris and ending in New York" do
674
+
675
+ before(:each) do
676
+ @start = Time.now.utc.in_time_zone("Europe/Paris")
677
+ @finish = Time.now.utc.in_time_zone("America/New_York")
678
+ cal = RiCal.Calendar do |ical|
679
+ ical.event do |ievent|
680
+ ievent.dtstart @start
681
+ ievent.dtend @finish
682
+ end
683
+ end
684
+ @event = cal.events.first
685
+ end
686
+
687
+ it "should have the right time zone for dtstart" do
688
+ @event.dtstart.tzid.should == "Europe/Paris"
689
+ end
690
+
691
+ it "should produce a TimeWithZone for dtstart" do
692
+ @event.dtstart.should be_instance_of(RiCal::TimeWithZone)
693
+ end
694
+
695
+ # ActiveRecord::TimeWithZone doesn't implement == as expected
696
+ it "should produce a dtstart which looks like the provided value" do
697
+ @event.dtstart.to_s.should == @start.to_s
698
+ end
699
+
700
+ it "should have the right time zone for dtend" do
701
+ @event.dtend.tzid.should == "America/New_York"
702
+ end
703
+
704
+ it "should produce a TimeWithZone for dtend" do
705
+ @event.dtend.should be_instance_of(RiCal::TimeWithZone)
706
+ end
707
+
708
+ # ActiveRecord::TimeWithZone doesn't implement == as expected
709
+ it "should produce a dtend which looks like the provided value" do
710
+ @event.dtend.to_s.should == @finish.to_s
711
+ end
712
+ end
713
+ end
714
+ end
715
+
716
+ context "An event with a floating start" do
717
+
718
+ before(:each) do
719
+ cal = RiCal.Calendar do |ical|
720
+ ical.event do |ievent|
721
+ ievent.dtstart "20090530T120000"
722
+ end
723
+ end
724
+ @event = cal.events.first
725
+ end
726
+
727
+ it "should produce a DateTime for dtstart" do
728
+ @event.dtstart.should be_instance_of(DateTime)
729
+ end
730
+
731
+ it "should have a floating dtstart" do
732
+ @event.dtstart.should have_floating_timezone
733
+ end
734
+ end
735
+ end