rubyredrick-ri_cal 0.0.2

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 (123) hide show
  1. data/History.txt +3 -0
  2. data/Manifest.txt +122 -0
  3. data/README.txt +271 -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 +2 -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/component/alarm.rb +22 -0
  21. data/lib/ri_cal/component/calendar.rb +199 -0
  22. data/lib/ri_cal/component/event.rb +30 -0
  23. data/lib/ri_cal/component/freebusy.rb +19 -0
  24. data/lib/ri_cal/component/journal.rb +22 -0
  25. data/lib/ri_cal/component/t_z_info_timezone.rb +124 -0
  26. data/lib/ri_cal/component/timezone/daylight_period.rb +26 -0
  27. data/lib/ri_cal/component/timezone/standard_period.rb +24 -0
  28. data/lib/ri_cal/component/timezone/timezone_period.rb +54 -0
  29. data/lib/ri_cal/component/timezone.rb +193 -0
  30. data/lib/ri_cal/component/todo.rb +26 -0
  31. data/lib/ri_cal/component.rb +224 -0
  32. data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
  33. data/lib/ri_cal/core_extensions/array.rb +7 -0
  34. data/lib/ri_cal/core_extensions/date/conversions.rb +27 -0
  35. data/lib/ri_cal/core_extensions/date.rb +13 -0
  36. data/lib/ri_cal/core_extensions/date_time/conversions.rb +27 -0
  37. data/lib/ri_cal/core_extensions/date_time.rb +13 -0
  38. data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
  39. data/lib/ri_cal/core_extensions/object.rb +8 -0
  40. data/lib/ri_cal/core_extensions/string/conversions.rb +32 -0
  41. data/lib/ri_cal/core_extensions/string.rb +8 -0
  42. data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
  43. data/lib/ri_cal/core_extensions/time/conversions.rb +27 -0
  44. data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +88 -0
  45. data/lib/ri_cal/core_extensions/time.rb +11 -0
  46. data/lib/ri_cal/core_extensions.rb +6 -0
  47. data/lib/ri_cal/invalid_timezone_identifer.rb +20 -0
  48. data/lib/ri_cal/occurrence_enumerator.rb +172 -0
  49. data/lib/ri_cal/parser.rb +138 -0
  50. data/lib/ri_cal/properties/alarm.rb +390 -0
  51. data/lib/ri_cal/properties/calendar.rb +164 -0
  52. data/lib/ri_cal/properties/event.rb +1526 -0
  53. data/lib/ri_cal/properties/freebusy.rb +594 -0
  54. data/lib/ri_cal/properties/journal.rb +1240 -0
  55. data/lib/ri_cal/properties/timezone.rb +151 -0
  56. data/lib/ri_cal/properties/timezone_period.rb +416 -0
  57. data/lib/ri_cal/properties/todo.rb +1562 -0
  58. data/lib/ri_cal/property_value/array.rb +19 -0
  59. data/lib/ri_cal/property_value/cal_address.rb +12 -0
  60. data/lib/ri_cal/property_value/date.rb +119 -0
  61. data/lib/ri_cal/property_value/date_time/additive_methods.rb +43 -0
  62. data/lib/ri_cal/property_value/date_time/time_machine.rb +180 -0
  63. data/lib/ri_cal/property_value/date_time/timezone_support.rb +65 -0
  64. data/lib/ri_cal/property_value/date_time.rb +324 -0
  65. data/lib/ri_cal/property_value/duration.rb +106 -0
  66. data/lib/ri_cal/property_value/geo.rb +12 -0
  67. data/lib/ri_cal/property_value/integer.rb +13 -0
  68. data/lib/ri_cal/property_value/occurrence_list.rb +82 -0
  69. data/lib/ri_cal/property_value/period.rb +63 -0
  70. data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +98 -0
  71. data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +77 -0
  72. data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +149 -0
  73. data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +54 -0
  74. data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +32 -0
  75. data/lib/ri_cal/property_value/recurrence_rule/occurence_incrementer.rb +794 -0
  76. data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +132 -0
  77. data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +61 -0
  78. data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +34 -0
  79. data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +50 -0
  80. data/lib/ri_cal/property_value/recurrence_rule/validations.rb +126 -0
  81. data/lib/ri_cal/property_value/recurrence_rule.rb +146 -0
  82. data/lib/ri_cal/property_value/text.rb +41 -0
  83. data/lib/ri_cal/property_value/uri.rb +12 -0
  84. data/lib/ri_cal/property_value/utc_offset.rb +34 -0
  85. data/lib/ri_cal/property_value.rb +110 -0
  86. data/lib/ri_cal/required_timezones.rb +56 -0
  87. data/lib/ri_cal/time_with_floating_timezone.rb +59 -0
  88. data/lib/ri_cal.rb +134 -0
  89. data/ri_cal.gemspec +47 -0
  90. data/sample_ical_files/from_ical_dot_app/test1.ics +38 -0
  91. data/script/console +10 -0
  92. data/script/destroy +14 -0
  93. data/script/generate +14 -0
  94. data/script/txt2html +71 -0
  95. data/spec/ri_cal/component/alarm_spec.rb +13 -0
  96. data/spec/ri_cal/component/calendar_spec.rb +55 -0
  97. data/spec/ri_cal/component/event_spec.rb +157 -0
  98. data/spec/ri_cal/component/freebusy_spec.rb +13 -0
  99. data/spec/ri_cal/component/journal_spec.rb +13 -0
  100. data/spec/ri_cal/component/t_z_info_timezone_spec.rb +37 -0
  101. data/spec/ri_cal/component/timezone_spec.rb +155 -0
  102. data/spec/ri_cal/component/todo_spec.rb +61 -0
  103. data/spec/ri_cal/component_spec.rb +212 -0
  104. data/spec/ri_cal/core_extensions/time/calculations_spec.rb +189 -0
  105. data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +46 -0
  106. data/spec/ri_cal/occurrence_enumerator_spec.rb +218 -0
  107. data/spec/ri_cal/parser_spec.rb +304 -0
  108. data/spec/ri_cal/property_value/date_spec.rb +22 -0
  109. data/spec/ri_cal/property_value/date_time_spec.rb +448 -0
  110. data/spec/ri_cal/property_value/duration_spec.rb +80 -0
  111. data/spec/ri_cal/property_value/period_spec.rb +50 -0
  112. data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +22 -0
  113. data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1815 -0
  114. data/spec/ri_cal/property_value/text_spec.rb +14 -0
  115. data/spec/ri_cal/property_value/utc_offset_spec.rb +49 -0
  116. data/spec/ri_cal/property_value_spec.rb +111 -0
  117. data/spec/ri_cal/required_timezones_spec.rb +68 -0
  118. data/spec/ri_cal_spec.rb +54 -0
  119. data/spec/spec.opts +4 -0
  120. data/spec/spec_helper.rb +24 -0
  121. data/tasks/ri_cal.rake +403 -0
  122. data/tasks/spec.rake +35 -0
  123. metadata +201 -0
@@ -0,0 +1,324 @@
1
+ require 'date'
2
+ module RiCal
3
+ class PropertyValue
4
+ #- ©2009 Rick DeNatale
5
+ #- All rights reserved. Refer to the file README.txt for the license
6
+ #
7
+ # RiCal::PropertyValue::CalAddress represents an icalendar CalAddress property value
8
+ # which is defined in RFC 2445 section 4.3.5 pp 35-37
9
+ class DateTime < PropertyValue
10
+
11
+ Dir[File.dirname(__FILE__) + "/date_time/*.rb"].sort.each do |path|
12
+ require path
13
+ end
14
+
15
+ include Comparable
16
+ include AdditiveMethods
17
+ include TimezoneSupport
18
+ include TimeMachine
19
+
20
+ # def initialize(timezone_finder, options={}) #:nodoc:
21
+ # super(timezone_finder ? timezone_finder : Calendar.new, options)
22
+ # end
23
+ #
24
+ def self.or_date(parent, line) # :nodoc:
25
+ if /T/.match(line[:value] || "")
26
+ new(parent, line)
27
+ else
28
+ PropertyValue::Date.new(parent, line)
29
+ end
30
+ end
31
+
32
+ def self.default_tzid # :nodoc:
33
+ @default_tzid ||= "UTC"
34
+ end
35
+
36
+ def self.params_for_tzid(tzid) #:nodoc:
37
+ if tzid == FloatingTimezone
38
+ {}
39
+ else
40
+ {'TZID' => tzid}
41
+ end
42
+ end
43
+
44
+ # Set the default tzid to be used when instantiating an instance from a ruby object
45
+ # see RiCal::PropertyValue::DateTime.from_time
46
+ #
47
+ # The parameter tzid is a string value to be used for the default tzid, a value of 'none' will cause
48
+ # values with NO timezone to be produced, which will be interpreted by iCalendar as floating times
49
+ # i.e. they are interpreted in the timezone of each client. Floating times are typically used
50
+ # to represent events which are 'repeated' in the various time zones, like the first hour of the year.
51
+ def self.default_tzid=(tzid)
52
+ @default_tzid = value
53
+ end
54
+
55
+ def self.default_tzid_hash # :nodoc:
56
+ if default_tzid.to_s == 'none'
57
+ {}
58
+ else
59
+ {'TZID' => default_tzid}
60
+ end
61
+ end
62
+
63
+ def inspect # :nodoc:
64
+ "#{@date_time_value}:#{tzid}"
65
+ end
66
+
67
+ # Returns the value of the receiver as an RFC 2445 iCalendar string
68
+ def value
69
+ if @date_time_value
70
+ @date_time_value.strftime("%Y%m%dT%H%M%S#{tzid == "UTC" ? "Z" : ""}")
71
+ else
72
+ nil
73
+ end
74
+ end
75
+
76
+ # Set the value of the property to val
77
+ #
78
+ # val may be either:
79
+ #
80
+ # * A string which can be parsed as a DateTime
81
+ # * A Time instance
82
+ # * A Date instance
83
+ # * A DateTime instance
84
+ def value=(val) # :nodoc:
85
+ case val
86
+ when nil
87
+ @date_time_value = nil
88
+ when String
89
+ self.tzid = 'UTC' if val =~/Z/
90
+ @date_time_value = ::DateTime.parse(val)
91
+ when ::DateTime
92
+ @date_time_value = val
93
+ when ::Date, ::Time
94
+ @date_time_value = ::DateTime.parse(val.to_s)
95
+ end
96
+ end
97
+
98
+ # Extract the time and timezone identifier from an object used to set the value of a DATETIME property.
99
+ #
100
+ # If the object is an array it is expected to have a time or datetime as its first element, and a time zone
101
+ # identifier string as the second element
102
+ #
103
+ # Otherwise determine if the object acts like an activesupport enhanced time, and extract its timezone
104
+ # idenfifier if it has one.
105
+ #
106
+ def self.time_and_tzid(object)
107
+ if ::Array === object
108
+ object, identifier = object[0], object[1]
109
+ else
110
+ activesupport_time = object.acts_like_time? rescue nil
111
+ time_zone = activesupport_time && object.time_zone rescue nil
112
+ identifier = time_zone && (time_zone.respond_to?(:tzinfo) ? time_zone.tzinfo : time_zone).identifier
113
+ end
114
+ [object, identifier]
115
+ end
116
+
117
+ # A hack to detect whether an array passed to convert is a
118
+ def self.single_time_or_date?(ruby_object)
119
+ if (::Array === ruby_object)
120
+ if (ruby_object.length == 2) && (::String === ruby_object[1])
121
+ case ruby_object[0]
122
+ when ::Date, ::DateTime, ::Time, PropertyValue::Date, PropertyValue::DateTime
123
+ ruby_object
124
+ else
125
+ nil
126
+ end
127
+ end
128
+ else
129
+ ruby_object
130
+ end
131
+ end
132
+
133
+
134
+ def self.convert(timezone_finder, ruby_object) # :nodoc:
135
+ convert_with_tzid_or_nil(timezone_finder, ruby_object) || ruby_object.to_ri_cal_date_or_date_time_value.for_parent(timezone_finder)
136
+ end
137
+
138
+ # Create an instance of RiCal::PropertyValue::DateTime representing a Ruby Time or DateTime
139
+ # If the ruby object has been extended by ActiveSupport to have a time_zone method, then
140
+ # the timezone will be used as the TZID parameter.
141
+ #
142
+ # Otherwise the class level default tzid will be used.
143
+ # == See
144
+ # * RiCal::PropertyValue::DateTime.default_tzid
145
+ # * RiCal::PropertyValue::DateTime#object_time_zone
146
+ def self.from_time(time_or_date_time)
147
+ convert_with_tzid_or_nil(nil, time_or_date_time) ||
148
+ new(nil, :value => time_or_date_time.strftime("%Y%m%dT%H%M%S"), :params => default_tzid_hash)
149
+ end
150
+
151
+ def self.convert_with_tzid_or_nil(timezone_finder, ruby_object) # :nodoc:
152
+ time, identifier = *self.time_and_tzid(ruby_object)
153
+ if identifier
154
+ new(
155
+ timezone_finder,
156
+ :params => params_for_tzid(identifier),
157
+ :value => time.strftime("%Y%m%d%H%M%S")
158
+ )
159
+ else
160
+ nil
161
+ end
162
+ end
163
+
164
+ def self.from_string(string) # :nodoc:
165
+ if string.match(/Z$/)
166
+ new(nil, :value => string, :tzid => 'UTC')
167
+ else
168
+ new(nil, :value => string)
169
+ end
170
+ end
171
+
172
+ def for_parent(parent) #:nodoc:
173
+ if timezone_finder.nil?
174
+ @timezone_finder = parent
175
+ self
176
+ elsif parent == timezone_finder
177
+ self
178
+ else
179
+ DateTime.new(parent, :value => @date_time_value, :params => params, :tzid => tzid)
180
+ end
181
+ end
182
+
183
+ def visible_params # :nodoc:
184
+ result = {"VALUE" => "DATE-TIME"}.merge(params)
185
+ if has_local_timezone?
186
+ result['TZID'] = tzid
187
+ else
188
+ result.delete('TZID')
189
+ end
190
+ result
191
+ end
192
+
193
+ def params=(value) #:nodoc:
194
+ @params = value.dup
195
+ if params_timezone = params['TZID']
196
+ @tzid = params_timezone
197
+ end
198
+ end
199
+
200
+ # Compare the receiver with another object which must respond to the to_datetime message
201
+ # The comparison is done using the Ruby DateTime representations of the two objects
202
+ def <=>(other)
203
+ @date_time_value <=> other.to_datetime
204
+ end
205
+
206
+ # Determine if the receiver and other are in the same month
207
+ def in_same_month_as?(other)
208
+ [other.year, other.month] == [year, month]
209
+ end
210
+
211
+ def nth_wday_in_month(n, which_wday)
212
+ @date_time_value.nth_wday_in_month(n, which_wday, self)
213
+ end
214
+
215
+ def nth_wday_in_year(n, which_wday)
216
+ @date_time_value.nth_wday_in_year(n, which_wday, self)
217
+ end
218
+
219
+ def self.civil(year, month, day, hour, min, sec, offset, start, params) #:nodoc:
220
+ PropertyValue::DateTime.new(
221
+ :value => ::DateTime.civil(year, month, day, hour, min, sec, offset, start),
222
+ :params =>(params ? params.dup : nil)
223
+ )
224
+ end
225
+
226
+ # Return the number of days in the month containing the receiver
227
+ def days_in_month
228
+ @date_time_value.days_in_month
229
+ end
230
+
231
+ def in_same_month_as?(other)
232
+ [other.year, other.month] == [year, month]
233
+ end
234
+
235
+
236
+
237
+ # Determine if the receiver and another object are equivalent RiCal::PropertyValue::DateTime instances
238
+ def ==(other)
239
+ if self.class === other
240
+ self.value == other.value && self.visible_params == other.visible_params && self.tzid == other.tzid
241
+ else
242
+ super
243
+ end
244
+ end
245
+
246
+ # TODO: consider if this should be a period rather than a hash
247
+ def occurrence_hash(default_duration) # :nodoc:
248
+ {:start => self, :end => (default_duration ? self + default_duration : nil)}
249
+ end
250
+
251
+ # Return the year (including the century)
252
+ def year
253
+ @date_time_value.year
254
+ end
255
+
256
+ # Return the month of the year (1..12)
257
+ def month
258
+ @date_time_value.month
259
+ end
260
+
261
+ # Return the day of the month
262
+ def day
263
+ @date_time_value.day
264
+ end
265
+
266
+ alias_method :mday, :day
267
+
268
+ # Return the day of the week
269
+ def wday
270
+ @date_time_value.wday
271
+ end
272
+
273
+ # Return the hour
274
+ def hour
275
+ @date_time_value.hour
276
+ end
277
+
278
+ # Return the minute
279
+ def min
280
+ @date_time_value.min
281
+ end
282
+
283
+ # Return the second
284
+ def sec
285
+ @date_time_value.sec
286
+ end
287
+
288
+
289
+ # Return an RiCal::PropertyValue::DateTime representing the receiver.
290
+ def to_ri_cal_date_time_value
291
+ self
292
+ end
293
+
294
+ def iso_year_and_week_one_start(wkst) #:nodoc:
295
+ @date_time_value.iso_year_and_week_one_start(wkst)
296
+ end
297
+
298
+ def iso_weeks_in_year(wkst)
299
+ @date_time_value.iso_weeks_in_year(wkst)
300
+ end
301
+
302
+ # Return the "Natural' property value for the receover, in this case the receiver itself."
303
+ def to_ri_cal_date_or_date_time_value
304
+ self
305
+ end
306
+
307
+ # Return the Ruby DateTime representation of the receiver
308
+ def to_datetime
309
+ @date_time_value
310
+ end
311
+
312
+ # Returns a ruby DateTime object representing the receiver.
313
+ def ruby_value
314
+ to_datetime
315
+ end
316
+
317
+ alias_method :to_ri_cal_ruby_value, :ruby_value
318
+
319
+ def add_date_times_to(required_timezones) #:nodoc:
320
+ required_timezones.add_datetime(self, tzid) if has_local_timezone?
321
+ end
322
+ end
323
+ end
324
+ end
@@ -0,0 +1,106 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale
4
+ #- All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # RiCal::PropertyValue::CalAddress represents an icalendar Duration property value
7
+ # which is defined in
8
+ # rfc 2445 section 4.3.6 p 37
9
+ class Duration < PropertyValue
10
+
11
+ def self.value_part(unit, diff) # :nodoc:
12
+ (diff == 0) ? nil : "#{diff}#{unit}"
13
+ end
14
+
15
+ def self.from_datetimes(parent, start, finish, sign='+') # :nodoc:
16
+ if start > finish
17
+ from_datetimes(finish, start, '-')
18
+ else
19
+ diff = finish - start
20
+ days_diff = diff.to_i
21
+ hours = (diff - days_diff) * 24
22
+ hour_diff = hours.to_i
23
+ minutes = (hours - hour_diff) * 60
24
+ min_diff = minutes.to_i
25
+ seconds = (minutes - min_diff) * 60
26
+ sec_diff = seconds.to_i
27
+
28
+ day_part = value_part('D',days_diff)
29
+ hour_part = value_part('H', hour_diff)
30
+ min_part = value_part('M', min_diff)
31
+ sec_part = value_part('S', sec_diff)
32
+ new(parent, :value => "#{sign}P#{day_part}T#{day_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.for_parent(parent)
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 days # :nodoc:
65
+ @days * @sign
66
+ end
67
+
68
+ def weeks # :nodoc:
69
+ @weeks * @sign
70
+ end
71
+
72
+ def hours # :nodoc:
73
+ @hours * @sign
74
+ end
75
+
76
+ def minutes # :nodoc:
77
+ @minutes * @sign
78
+ end
79
+
80
+ def seconds # :nodoc:
81
+ @seconds * @sign
82
+ end
83
+
84
+ # Determine whether another object is an equivalent RiCal::PropertyValue::Duration
85
+ def ==(other)
86
+ other.kind_of?(PropertyValue::Duration) && value == other.value
87
+ end
88
+
89
+ # Returns the receiver
90
+ def to_ri_cal_duration_value
91
+ self
92
+ end
93
+
94
+ # Double-dispatch method to support RiCal::PropertyValue::DateTime.-
95
+ def subtract_from_date_time_value(date_time_value)
96
+ date_time_value.advance(:weeks => -weeks, :days => -days, :hours => -hours, :minutes => -minutes, :seconds => -seconds)
97
+ end
98
+
99
+ # Double-dispatch method to support RiCal::PropertyValue::DateTime.+
100
+ def add_to_date_time_value(date_time_value)
101
+ date_time_value.advance(:weeks => weeks, :days => days, :hours => hours, :minutes => minutes, :seconds => seconds)
102
+ end
103
+
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,12 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale
4
+ #- All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # RiCal::PropertyValue::CalAddress represents an icalendar Duration property value
7
+ # which is defined in
8
+ # rfc 2445 section 4.8.1.6 pp 82-83
9
+ class Geo < PropertyValue
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale
4
+ #- All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ class Integer < PropertyValue # :nodoc:
7
+
8
+ def value=(string)
9
+ @value = string.to_i
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,82 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ # OccurrenceList is used to represent the value of an RDATE or EXDATE property.
4
+ #- ©2009 Rick DeNatale
5
+ #- All rights reserved. Refer to the file README.txt for the license
6
+ #
7
+ class OccurrenceList < Array
8
+ class Enumerator # :nodoc:
9
+
10
+ attr_accessor :default_duration, :occurrence_list
11
+
12
+ # TODO: the component parameter should always be the parent
13
+ def initialize(occurrences, component) # :nodoc:
14
+ self.occurrence_list = occurrences
15
+ self.default_duration = component.default_duration
16
+ @index = 0
17
+ end
18
+
19
+ def next_occurrence
20
+ if @index < occurrence_list.length
21
+ result = occurrence_list[@index].occurrence_hash(default_duration)
22
+ @index += 1
23
+ result
24
+ else
25
+ nil
26
+ end
27
+ end
28
+ end
29
+
30
+
31
+ def self.convert(timezone_finder, ruby_object) # :nodoc:
32
+ if PropertyValue::DateTime.single_time_or_date?(ruby_object)
33
+ values = [ruby_object]
34
+ else
35
+ values = ruby_object
36
+ end
37
+ super(timezone_finder, values)
38
+ end
39
+
40
+ def value=(val) #:nodoc:
41
+ super
42
+ case params[:value]
43
+ when 'DATE-TIME', nil
44
+ @elements = @value.map {|val| PropertyValue::DateTime.convert(self, val)}.sort
45
+ @value = @elements.map {|element| element.value}
46
+ when 'DATE'
47
+ @elements = @value.map {|val| PropertyValue::Date.new(self, val)}.sort
48
+ @value = @elements.map {|element| element.value}
49
+ when 'PERIOD'
50
+ end
51
+ end
52
+ end
53
+
54
+ attr_writer :elements
55
+ private :elements=
56
+
57
+ def for_parent(parent)
58
+ if timezone_finder.nil?
59
+ @timezone_finder = parent
60
+ self
61
+ elsif timezone_finder == parent
62
+ self
63
+ else
64
+ OccurrenceList.new(parent, :value => value)
65
+ end
66
+ end
67
+
68
+ # Return an enumerator which can produce the elements of the occurrence list
69
+ def enumerator(component)
70
+ OccurrenceList::Enumerator.new(@elements, component)
71
+ end
72
+
73
+ def add_date_times_to(required_timezones) #:nodoc:
74
+ if @elements
75
+ @elements.each do | occurrence |
76
+ occurrence.add_date_times_to(required_timezones)
77
+ end
78
+ end
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,63 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale
4
+ #- All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # RiCal::PropertyValue::CalAddress represents an icalendar Period property value
7
+ # which is defined in
8
+ # rfc 2445 section 4.3.9 p 39
9
+ #
10
+ # Known bugs. This doesn't properly work when dtstart, dtend or duration are changed independently
11
+ class Period < PropertyValue
12
+
13
+ # The DATE-TIME on which the period starts
14
+ attr_accessor :dtstart
15
+ # The DATE-TIME on which the period ends
16
+ attr_accessor :dtend
17
+ # The DURATION of the period
18
+ attr_accessor :duration
19
+
20
+ def value=(string) # :nodoc:
21
+ starter, terminator = *string.split("/")
22
+ self.dtstart = PropertyValue::DateTime.new(self, :value => starter)
23
+ if /P/ =~ terminator
24
+ self.duration = PropertyValue::Duration.new(self, :value => terminator)
25
+ self.dtend = dtstart + duration
26
+ else
27
+ self.dtend = PropertyValue::DateTime.new(self, :value => terminator)
28
+ self.duration = PropertyValue::Duration.from_datetimes(self, dtstart.to_datetime, dtend.to_datetime)
29
+ end
30
+ end
31
+
32
+ def for_parent(parent)
33
+ if timezone_finder.nil
34
+ @timezone_finder = parent
35
+ self
36
+ elsif timezone_finder == parent
37
+ self
38
+ else
39
+ Period.new(parent, :value => value)
40
+ end
41
+ end
42
+
43
+ def self.convert(parent, ruby_object) # :nodoc:
44
+ ruby_object.to_ri_cal_period_value.for_parent(parent)
45
+ end
46
+
47
+ # return the receiver
48
+ def to_ri_cal_period_value
49
+ self
50
+ end
51
+
52
+ # TODO: consider if this should be a period rather than a hash
53
+ def occurrence_hash(default_duration) #:nodoc:
54
+ {:start => self, :end => (default_duration ? self + default_duration : nil)}
55
+ end
56
+
57
+ def add_date_times_to(required_timezones) #:nodoc:
58
+ dtstart.add_date_times_to(required_timezones)
59
+ dtend.add_date_times_to(required_timezones)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,98 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ class RecurrenceRule < PropertyValue
4
+ #- ©2009 Rick DeNatale
5
+ #- All rights reserved. Refer to the file README.txt for the license
6
+ #
7
+ module EnumerationSupportMethods # :nodoc:
8
+
9
+ # if the recurrence rule has a bysetpos part we need to search starting with the
10
+ # first time in the frequency period containing the start time specified by DTSTART
11
+ def adjust_start(start_time) # :nodoc:
12
+ if by_list[:bysetpos]
13
+ case freq
14
+ when "SECONDLY"
15
+ start_time
16
+ when "MINUTELY"
17
+ start_time.change(:seconds => 0)
18
+ when "HOURLY"
19
+ start_time.change(
20
+ :minutes => 0,
21
+ :seconds => start_time.sec
22
+ )
23
+ when "DAILY"
24
+ start_time.change(
25
+ :hour => 0,
26
+ :minutes => start_time.min,
27
+ :seconds => start_time.sec
28
+ )
29
+ when "WEEKLY"
30
+ start_of_week(time)
31
+ when "MONTHLY"
32
+ start_time.change(
33
+ :day => 1,
34
+ :hour => start_time.hour,
35
+ :minutes => start_time.min,
36
+ :seconds => start_time.sec
37
+ )
38
+ when "YEARLY"
39
+ start_time.change(
40
+ :month => 1,
41
+ :day => start_time.day,
42
+ :hour => start_time.hour,
43
+ :minutes => start_time.min,
44
+ :seconds => start_time.sec
45
+ )
46
+ end
47
+ else
48
+ start_time
49
+ end
50
+ end
51
+
52
+ def enumerator(component) # :nodoc:
53
+ Enumerator.for(self, component, by_list[:bysetpos])
54
+ end
55
+
56
+ def exhausted?(count, time) # :nodoc:
57
+ (@count && count > @count) || (@until && (time > @until))
58
+ end
59
+
60
+ def in_same_set?(time1, time2) # :nodoc:
61
+ case freq
62
+ when "SECONDLY"
63
+ [time1.year, time1.month, time1.day, time1.hour, time1.min, time1.sec] ==
64
+ [time2.year, time2.month, time2.day, time2.hour, time2.min, time2.sec]
65
+ when "MINUTELY"
66
+ [time1.year, time1.month, time1.day, time1.hour, time1.min] ==
67
+ [time2.year, time2.month, time2.day, time2.hour, time2.min]
68
+ when "HOURLY"
69
+ [time1.year, time1.month, time1.day, time1.hour] ==
70
+ [time2.year, time2.month, time2.day, time2.hour]
71
+ when "DAILY"
72
+ [time1.year, time1.month, time1.day] ==
73
+ [time2.year, time2.month, time2.day]
74
+ when "WEEKLY"
75
+ sow1 = start_of_week(time1)
76
+ sow2 = start_of_week(time2)
77
+ [sow1.year, sow1.month, sow1.day] ==
78
+ [sow2.year, sow2.month, sow2.day]
79
+ when "MONTHLY"
80
+ [time1.year, time1.month] ==
81
+ [time2.year, time2.month]
82
+ when "YEARLY"
83
+ time1.year == time2.year
84
+ end
85
+ end
86
+
87
+ def by_rule_list(which) # :nodoc:
88
+ if @by_list
89
+ @by_list[which]
90
+ else
91
+ nil
92
+ end
93
+ end
94
+
95
+ end
96
+ end
97
+ end
98
+ end