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,359 @@
1
+ require 'date'
2
+ module RiCal
3
+ class PropertyValue
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # RiCal::PropertyValue::CalAddress represents an icalendar CalAddress property value
7
+ # which is defined in RFC 2445 section 4.3.5 pp 35-37
8
+ class DateTime < PropertyValue
9
+
10
+ Dir[File.dirname(__FILE__) + "/date_time/*.rb"].sort.each do |path|
11
+ require path
12
+ end
13
+
14
+ include Comparable
15
+ include AdditiveMethods
16
+ include TimezoneSupport
17
+ include TimeMachine
18
+
19
+ def self.or_date(parent, line) # :nodoc:
20
+ if /T/.match(line[:value] || "")
21
+ new(parent, line)
22
+ else
23
+ PropertyValue::Date.new(parent, line)
24
+ end
25
+ end
26
+
27
+ def self.valid_string?(string) #:nodoc:
28
+ string =~ /^\d{8}T\d{6}Z?$/
29
+ end
30
+
31
+ def self.default_tzid # :nodoc:
32
+ @default_tzid ||= "UTC"
33
+ end
34
+
35
+ def self.params_for_tzid(tzid) #:nodoc:
36
+ if tzid == :floating
37
+ {}
38
+ else
39
+ {'TZID' => tzid}
40
+ end
41
+ end
42
+
43
+ # Set the default tzid to be used when instantiating an instance from a ruby object
44
+ # see RiCal::PropertyValue::DateTime.from_time
45
+ #
46
+ # The parameter tzid is a string value to be used for the default tzid, a value of :floating will cause
47
+ # values with NO timezone to be produced, which will be interpreted by iCalendar as floating times
48
+ # i.e. they are interpreted in the timezone of each client. Floating times are typically used
49
+ # to represent events which are 'repeated' in the various time zones, like the first hour of the year.
50
+ def self.default_tzid=(tzid)
51
+ @default_tzid = tzid
52
+ end
53
+
54
+ def self.default_tzid_hash # :nodoc:
55
+ if default_tzid.to_s == 'none'
56
+ {}
57
+ else
58
+ {'TZID' => default_tzid}
59
+ end
60
+ end
61
+
62
+ def inspect # :nodoc:
63
+ "#{@date_time_value}:#{tzid}"
64
+ end
65
+
66
+ # Returns the value of the receiver as an RFC 2445 iCalendar string
67
+ def value
68
+ if @date_time_value
69
+ "#{@date_time_value.ical_str}#{tzid == "UTC" ? "Z" : ""}"
70
+ else
71
+ nil
72
+ end
73
+ end
74
+
75
+ def to_ri_cal_zulu_date_time
76
+ ZuluDateTime.new(nil, :value => self.utc.fast_date_tme)
77
+ end
78
+
79
+ def fast_date_tme # :nodoc:
80
+ @date_time_value
81
+ end
82
+
83
+ # Set the value of the property to val
84
+ #
85
+ # val may be either:
86
+ #
87
+ # * A string which can be parsed as a DateTime
88
+ # * A Time instance
89
+ # * A Date instance
90
+ # * A DateTime instance
91
+ def value=(val) # :nodoc:
92
+ case val
93
+ when nil
94
+ @date_time_value = nil
95
+ when String
96
+ @date_time_value = FastDateTime.from_date_time(::DateTime.parse(val))
97
+ if val =~/Z/
98
+ self.tzid = 'UTC'
99
+ else
100
+ @tzid ||= :floating
101
+ end
102
+ when FastDateTime
103
+ @date_time_value = val
104
+ when ::DateTime
105
+ @date_time_value = FastDateTime.from_date_time(val)
106
+ when ::Date, ::Time
107
+ @date_time_value = FastDateTime.from_date_time(::DateTime.parse(val.to_s))
108
+ end
109
+ reset_cached_values
110
+ end
111
+
112
+ # Extract the time and timezone identifier from an object used to set the value of a DATETIME property.
113
+ #
114
+ # If the object is a string it should be of the form [TZID=identifier:]
115
+ #
116
+ # Otherwise determine if the object acts like an activesupport enhanced time, and extract its timezone
117
+ # idenfifier if it has one.
118
+ #
119
+ def self.time_and_parameters(object)
120
+ parameters = {}
121
+ if ::String === object
122
+ object, parameters = self.time_and_parameters_from_string(object)
123
+ else
124
+ identifier = object.tzid rescue nil
125
+ parameters["TZID"] = identifier if identifier
126
+ end
127
+ [object, parameters]
128
+ end
129
+
130
+
131
+ def self.convert(timezone_finder, ruby_object) # :nodoc:
132
+ ruby_object.to_ri_cal_date_or_date_time_value(timezone_finder)
133
+ end
134
+
135
+ def self.from_string(string) # :nodoc:
136
+ if string.match(/Z$/)
137
+ new(nil, :value => string, :tzid => 'UTC')
138
+ else
139
+ new(nil, :value => string)
140
+ end
141
+ end
142
+
143
+ def for_parent(parent) #:nodoc:
144
+ if timezone_finder.nil?
145
+ @timezone_finder = parent
146
+ self
147
+ elsif parent == timezone_finder
148
+ self
149
+ else
150
+ DateTime.new(parent, :value => @date_time_value, :params => params, :tzid => tzid)
151
+ end
152
+ end
153
+
154
+ def visible_params # :nodoc:
155
+ result = {"VALUE" => "DATE-TIME"}.merge(params)
156
+ if has_local_timezone?
157
+ result['TZID'] = tzid
158
+ else
159
+ result.delete('TZID')
160
+ end
161
+ result
162
+ end
163
+
164
+ def params=(value) #:nodoc:
165
+ @params = value.dup
166
+ if params_timezone = @params['TZID']
167
+ self.tzid = @params['TZID']
168
+ end
169
+ end
170
+
171
+ # Return a Hash representing this properties parameters
172
+ def params
173
+ result = @params.dup
174
+ case tzid
175
+ when :floating, nil, "UTC"
176
+ result.delete('TZID')
177
+ else
178
+ result['TZID'] = tzid
179
+ end
180
+ result
181
+ end
182
+
183
+ # Compare the receiver with another object which must respond to the to_datetime message
184
+ # The comparison is done using the Ruby DateTime representations of the two objects
185
+ def <=>(other)
186
+ other.cmp_fast_date_time_value(@date_time_value)
187
+ end
188
+
189
+ def cmp_fast_date_time_value(other)
190
+ other <=> @date_time_value
191
+ end
192
+
193
+ # Determine if the receiver and other are in the same month
194
+ def in_same_month_as?(other)
195
+ [other.year, other.month] == [year, month]
196
+ end
197
+
198
+ def with_date_time_value(date_time_value)
199
+ PropertyValue::DateTime.new(
200
+ timezone_finder,
201
+ :value => date_time_value,
202
+ :params => (params),
203
+ :tzid => tzid
204
+ )
205
+ end
206
+
207
+ def nth_wday_in_month(n, which_wday) #:nodoc:
208
+ with_date_time_value(@date_time_value.nth_wday_in_month(n, which_wday))
209
+ end
210
+
211
+ def nth_wday_in_year(n, which_wday) #:nodoc:
212
+ with_date_time_value(@date_time_value.nth_wday_in_year(n, which_wday))
213
+ end
214
+
215
+ def self.civil(year, month, day, hour, min, sec, offset, start, params) #:nodoc:
216
+ PropertyValue::DateTime.new(
217
+ :value => ::DateTime.civil(year, month, day, hour, min, sec, offset, start),
218
+ :params =>(params ? params.dup : nil)
219
+ )
220
+ end
221
+
222
+ # Return the number of days in the month containing the receiver
223
+ def days_in_month
224
+ @date_time_value.days_in_month
225
+ end
226
+
227
+ # Determine if the receiver and another object are equivalent RiCal::PropertyValue::DateTime instances
228
+ def ==(other)
229
+ if self.class === other
230
+ self.value == other.value && self.visible_params == other.visible_params && self.tzid == other.tzid
231
+ else
232
+ super
233
+ end
234
+ end
235
+
236
+ # TODO: consider if this should be a period rather than a hash
237
+ def occurrence_period(default_duration) # :nodoc:
238
+ RiCal::OccurrencePeriod.new(self, (default_duration ? self + default_duration : nil))
239
+ end
240
+
241
+ # Return the year (including the century)
242
+ def year
243
+ @date_time_value.year
244
+ end
245
+
246
+ # Return the month of the year (1..12)
247
+ def month
248
+ @date_time_value.month
249
+ end
250
+
251
+ # Return the day of the month
252
+ def day
253
+ @date_time_value.day
254
+ end
255
+
256
+ alias_method :mday, :day
257
+
258
+ # Return the day of the week
259
+ def wday
260
+ @date_time_value.wday
261
+ end
262
+
263
+ # Return the hour
264
+ def hour
265
+ @date_time_value.hour
266
+ end
267
+
268
+ # Return the minute
269
+ def min
270
+ @date_time_value.min
271
+ end
272
+
273
+ # Return the second
274
+ def sec
275
+ @date_time_value.sec
276
+ end
277
+
278
+
279
+ # Return an RiCal::PropertyValue::DateTime representing the receiver.
280
+ def to_ri_cal_date_time_value(timezone=nil)
281
+ for_parent(timezone)
282
+ end
283
+
284
+ def iso_year_and_week_one_start(wkst) #:nodoc:
285
+ @date_time_value.iso_year_and_week_one_start(wkst)
286
+ end
287
+
288
+ def iso_weeks_in_year(wkst) #:nodoc:
289
+ @date_time_value.iso_weeks_in_year(wkst) #:nodoc:
290
+ end
291
+
292
+ # Return the "Natural' property value for the receover, in this case the receiver itself."
293
+ def to_ri_cal_date_or_date_time_value(timezone_finder = nil) #:nodoc:
294
+ self.for_parent(timezone_finder)
295
+ end
296
+
297
+ # Return a Date property for this DateTime
298
+ def to_ri_cal_date_value(timezone_finder=nil)
299
+ PropertyValue::Date.new(timezone_finder, :value => @date_time_value.ical_date_str)
300
+ end
301
+
302
+ # Return the Ruby DateTime representation of the receiver
303
+ def to_datetime #:nodoc:
304
+ @date_time_value.to_datetime
305
+ end
306
+
307
+ # Returns a ruby DateTime object representing the receiver.
308
+ def ruby_value
309
+ if has_valid_tzinfo_tzid? && RiCal::TimeWithZone && tz_info_source?
310
+ RiCal::TimeWithZone.new(utc.to_datetime, ::Time.__send__(:get_zone, @tzid))
311
+ else
312
+ ::DateTime.civil(year, month, day, hour, min, sec, rational_tz_offset).set_tzid(@tzid)
313
+ end
314
+ end
315
+
316
+ alias_method :to_ri_cal_ruby_value, :to_datetime
317
+ alias_method :to_finish_time, :ruby_value
318
+
319
+ def to_zulu_time
320
+ utc.to_datetime
321
+ end
322
+
323
+ # If a time is floating, then the utc of it's start time may actually be as early
324
+ # as 12 hours earlier if the occurrence is being viewed in a time zone just west
325
+ # of the International Date Line
326
+ def to_zulu_occurrence_range_start_time
327
+ if floating?
328
+ @date_time_value.advance(:hours => -12, :offset => 0).to_datetime
329
+ else
330
+ to_zulu_time
331
+ end
332
+ end
333
+
334
+
335
+ # If a time is floating, then the utc of it's start time may actually be as early
336
+ # as 12 hours later if the occurrence is being viewed in a time zone just east
337
+ # of the International Date Line
338
+ def to_zulu_occurrence_range_finish_time
339
+ if floating?
340
+ utc.advance(:hours => 12).to_datetime
341
+ else
342
+ to_zulu_time
343
+ end
344
+ end
345
+
346
+ def add_date_times_to(required_timezones) #:nodoc:
347
+ required_timezones.add_datetime(self, tzid) if has_local_timezone?
348
+ end
349
+
350
+ def start_of_day?
351
+ [hour, min, sec] == [0,0,0]
352
+ end
353
+
354
+ def for_occurrence(occurrence)
355
+ occurrence.to_ri_cal_date_time_value(timezone_finder)
356
+ end
357
+ end
358
+ end
359
+ end
@@ -0,0 +1,110 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # RiCal::PropertyValue::CalAddress represents an icalendar Duration property value
6
+ # which is defined in
7
+ # rfc 2445 section 4.3.6 p 37
8
+ class Duration < PropertyValue
9
+
10
+ def self.value_part(unit, diff) # :nodoc:
11
+ (diff == 0) ? nil : "#{diff}#{unit}"
12
+ end
13
+
14
+ def self.from_datetimes(parent, start, finish, sign='+') # :nodoc:
15
+ if start > finish
16
+ from_datetimes(self, finish, start, '-')
17
+ else
18
+ diff = finish - start
19
+ days_diff = diff.to_i
20
+ hours = (diff - days_diff) * 24
21
+ hour_diff = hours.to_i
22
+ minutes = (hours - hour_diff) * 60
23
+ min_diff = minutes.to_i
24
+ seconds = (minutes - min_diff) * 60
25
+ sec_diff = seconds.to_i
26
+
27
+ day_part = value_part('D',days_diff)
28
+ hour_part = value_part('H', hour_diff)
29
+ min_part = value_part('M', min_diff)
30
+ sec_part = value_part('S', sec_diff)
31
+ t_part = (hour_diff.abs + min_diff.abs + sec_diff.abs) == 0 ? "" : "T"
32
+ new(parent, :value => "#{sign}P#{day_part}#{t_part}#{hour_part}#{min_part}#{sec_part}")
33
+ end
34
+ end
35
+
36
+ def self.convert(parent, ruby_object) # :nodoc:
37
+ ruby_object.to_ri_cal_duration_value
38
+ end
39
+
40
+ def value=(string) # :nodoc:
41
+ super
42
+ match = /([+-])?P(.*)$/.match(string)
43
+ @days = @hours = @minutes = @seconds = @weeks = 0
44
+ if match
45
+ @sign = match[1] == '-' ? -1 : 1
46
+ match[2].scan(/(\d+)([DHMSW])/) do |digits, unit|
47
+ number = digits.to_i
48
+ case unit
49
+ when 'D'
50
+ @days = number
51
+ when 'H'
52
+ @hours = number
53
+ when 'M'
54
+ @minutes = number
55
+ when 'S'
56
+ @seconds = number
57
+ when 'W'
58
+ @weeks = number
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def self.valid_string?(string) #:nodoc:
65
+ string =~ /^[+-]?P((\d+D)(T((\d+)[HMS])+)?)|(T((\d+)[HMS])+)|(\d+W)$/
66
+ end
67
+
68
+ def days # :nodoc:
69
+ @days * @sign
70
+ end
71
+
72
+ def weeks # :nodoc:
73
+ @weeks * @sign
74
+ end
75
+
76
+ def hours # :nodoc:
77
+ @hours * @sign
78
+ end
79
+
80
+ def minutes # :nodoc:
81
+ @minutes * @sign
82
+ end
83
+
84
+ def seconds # :nodoc:
85
+ @seconds * @sign
86
+ end
87
+
88
+ # Determine whether another object is an equivalent RiCal::PropertyValue::Duration
89
+ def ==(other)
90
+ other.kind_of?(PropertyValue::Duration) && value == other.value
91
+ end
92
+
93
+ # Returns the receiver
94
+ def to_ri_cal_duration_value
95
+ self
96
+ end
97
+
98
+ # Double-dispatch method to support RiCal::PropertyValue::DateTime.-
99
+ def subtract_from_date_time_value(date_time_value)
100
+ date_time_value.advance(:weeks => -weeks, :days => -days, :hours => -hours, :minutes => -minutes, :seconds => -seconds)
101
+ end
102
+
103
+ # Double-dispatch method to support RiCal::PropertyValue::DateTime.+
104
+ def add_to_date_time_value(date_time_value)
105
+ date_time_value.advance(:weeks => weeks, :days => days, :hours => hours, :minutes => minutes, :seconds => seconds)
106
+ end
107
+
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,11 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # RiCal::PropertyValue::CalAddress represents an icalendar Duration property value
6
+ # which is defined in
7
+ # rfc 2445 section 4.8.1.6 pp 82-83
8
+ class Geo < PropertyValue
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ class Integer < PropertyValue # :nodoc:
6
+
7
+ def value=(string)
8
+ @value = string.to_i
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,144 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ # OccurrenceList is used to represent the value of an RDATE or EXDATE property.
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ class OccurrenceList < Array
7
+ attr_accessor :tzid #:nodoc:
8
+
9
+ class Enumerator # :nodoc:
10
+
11
+ attr_accessor :default_duration, :occurrence_list
12
+
13
+ # TODO: the component parameter should always be the parent
14
+ def initialize(occurrences, component) # :nodoc:
15
+ self.occurrence_list = occurrences
16
+ self.default_duration = component.default_duration
17
+ @index = 0
18
+ end
19
+
20
+ def bounded?
21
+ true
22
+ end
23
+
24
+ def empty?
25
+ occurrence_list.empty?
26
+ end
27
+
28
+ def next_occurrence
29
+ if @index < occurrence_list.length
30
+ result = occurrence_list[@index].occurrence_period(default_duration)
31
+ @index += 1
32
+ result
33
+ else
34
+ nil
35
+ end
36
+ end
37
+ end
38
+
39
+ def initialize(timezone_finder, options={}) # :nodoc:
40
+ super
41
+ validate_elements
42
+ end
43
+
44
+ def self.convert(timezone_finder, *ruby_objects) # :nodoc:
45
+ # ruby_objects = [ruby_objects] unless Array === ruby_objects
46
+ source_elements = ruby_objects.inject([]) { |se, element|
47
+ if String === element
48
+ element.split(",").each {|str| se << str}
49
+ else
50
+ se << element
51
+ end
52
+ se
53
+ }
54
+ new(timezone_finder, :source_elements => source_elements )
55
+ end
56
+
57
+ def values_to_elements(values) # :nodoc:
58
+ values.map {|val| val.to_ri_cal_occurrence_list_value(self)}
59
+ end
60
+
61
+ def tzid_from_source_elements # :nodoc:
62
+ if @source_elements && String === (first_source = @source_elements.first)
63
+ probe = first_source.to_ri_cal_occurrence_list_value rescue nil
64
+ unless probe
65
+ return @source_elements.shift
66
+ end
67
+ end
68
+ nil
69
+ end
70
+
71
+ def tzid_conflict(element_tzid) # :nodoc:
72
+ element_tzid && tzid != element_tzid
73
+ end
74
+
75
+ def validate_elements # :nodoc:
76
+ if @source_elements
77
+ self.tzid = tzid_from_source_elements
78
+ @elements = values_to_elements(@source_elements)
79
+ @value = @elements.map {|prop| prop.value}
80
+ else
81
+ @elements = values_to_elements(@value)
82
+ self.tzid = params['TZID']
83
+ end
84
+ # if the tzid wasn't set by the parameters
85
+ self.tzid ||= @elements.map {|element| element.tzid}.find {|id| id}
86
+ @elements.each do |element|
87
+ raise InvalidPropertyValue.new("Mixed timezones are not allowed in an occurrence list") if tzid_conflict(element.tzid)
88
+ element.tzid = tzid
89
+ end
90
+ end
91
+
92
+ def has_local_timezone? # :nodoc:
93
+ tzid && tzid != 'UTC'
94
+ end
95
+
96
+ def visible_params # :nodoc:
97
+ result = params.dup
98
+ if has_local_timezone?
99
+ result['TZID'] = tzid
100
+ else
101
+ result.delete('TZID')
102
+ end
103
+ result
104
+ end
105
+
106
+ def value # :nodoc:
107
+ @elements.map {|element| element.value}.join(",")
108
+ end
109
+
110
+ # Return an array of the occurrences within the list
111
+ def ruby_value
112
+ @elements.map {|prop| prop.ruby_value}
113
+ end
114
+ end
115
+
116
+ attr_accessor :elements, :source_elements #:nodoc:
117
+ private :elements, :elements=, :source_elements=, :source_elements
118
+
119
+ def for_parent(parent) #:nodoc:
120
+ if timezone_finder.nil?
121
+ @timezone_finder = parent
122
+ self
123
+ elsif timezone_finder == parent
124
+ self
125
+ else
126
+ OccurrenceList.new(parent, :value => value)
127
+ end
128
+ end
129
+
130
+ # Return an enumerator which can produce the elements of the occurrence list
131
+ def enumerator(component) # :nodoc:
132
+ OccurrenceList::Enumerator.new(@elements, component)
133
+ end
134
+
135
+ def add_date_times_to(required_timezones) #:nodoc:
136
+ if @elements
137
+ @elements.each do | occurrence |
138
+ occurrence.add_date_times_to(required_timezones)
139
+ end
140
+ end
141
+ end
142
+
143
+ end
144
+ end