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.
- data/History.txt +3 -0
- data/Manifest.txt +122 -0
- data/README.txt +271 -0
- data/Rakefile +31 -0
- data/bin/ri_cal +8 -0
- data/component_attributes/alarm.yml +10 -0
- data/component_attributes/calendar.yml +4 -0
- data/component_attributes/component_property_defs.yml +180 -0
- data/component_attributes/event.yml +45 -0
- data/component_attributes/freebusy.yml +16 -0
- data/component_attributes/journal.yml +35 -0
- data/component_attributes/timezone.yml +3 -0
- data/component_attributes/timezone_period.yml +11 -0
- data/component_attributes/todo.yml +46 -0
- data/copyrights.txt +2 -0
- data/docs/draft-ietf-calsify-2446bis-08.txt +7280 -0
- data/docs/draft-ietf-calsify-rfc2445bis-09.txt +10416 -0
- data/docs/incrementers.txt +7 -0
- data/docs/rfc2445.pdf +0 -0
- data/lib/ri_cal/component/alarm.rb +22 -0
- data/lib/ri_cal/component/calendar.rb +199 -0
- data/lib/ri_cal/component/event.rb +30 -0
- data/lib/ri_cal/component/freebusy.rb +19 -0
- data/lib/ri_cal/component/journal.rb +22 -0
- data/lib/ri_cal/component/t_z_info_timezone.rb +124 -0
- data/lib/ri_cal/component/timezone/daylight_period.rb +26 -0
- data/lib/ri_cal/component/timezone/standard_period.rb +24 -0
- data/lib/ri_cal/component/timezone/timezone_period.rb +54 -0
- data/lib/ri_cal/component/timezone.rb +193 -0
- data/lib/ri_cal/component/todo.rb +26 -0
- data/lib/ri_cal/component.rb +224 -0
- data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
- data/lib/ri_cal/core_extensions/array.rb +7 -0
- data/lib/ri_cal/core_extensions/date/conversions.rb +27 -0
- data/lib/ri_cal/core_extensions/date.rb +13 -0
- data/lib/ri_cal/core_extensions/date_time/conversions.rb +27 -0
- data/lib/ri_cal/core_extensions/date_time.rb +13 -0
- data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
- data/lib/ri_cal/core_extensions/object.rb +8 -0
- data/lib/ri_cal/core_extensions/string/conversions.rb +32 -0
- data/lib/ri_cal/core_extensions/string.rb +8 -0
- data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
- data/lib/ri_cal/core_extensions/time/conversions.rb +27 -0
- data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +88 -0
- data/lib/ri_cal/core_extensions/time.rb +11 -0
- data/lib/ri_cal/core_extensions.rb +6 -0
- data/lib/ri_cal/invalid_timezone_identifer.rb +20 -0
- data/lib/ri_cal/occurrence_enumerator.rb +172 -0
- data/lib/ri_cal/parser.rb +138 -0
- data/lib/ri_cal/properties/alarm.rb +390 -0
- data/lib/ri_cal/properties/calendar.rb +164 -0
- data/lib/ri_cal/properties/event.rb +1526 -0
- data/lib/ri_cal/properties/freebusy.rb +594 -0
- data/lib/ri_cal/properties/journal.rb +1240 -0
- data/lib/ri_cal/properties/timezone.rb +151 -0
- data/lib/ri_cal/properties/timezone_period.rb +416 -0
- data/lib/ri_cal/properties/todo.rb +1562 -0
- data/lib/ri_cal/property_value/array.rb +19 -0
- data/lib/ri_cal/property_value/cal_address.rb +12 -0
- data/lib/ri_cal/property_value/date.rb +119 -0
- data/lib/ri_cal/property_value/date_time/additive_methods.rb +43 -0
- data/lib/ri_cal/property_value/date_time/time_machine.rb +180 -0
- data/lib/ri_cal/property_value/date_time/timezone_support.rb +65 -0
- data/lib/ri_cal/property_value/date_time.rb +324 -0
- data/lib/ri_cal/property_value/duration.rb +106 -0
- data/lib/ri_cal/property_value/geo.rb +12 -0
- data/lib/ri_cal/property_value/integer.rb +13 -0
- data/lib/ri_cal/property_value/occurrence_list.rb +82 -0
- data/lib/ri_cal/property_value/period.rb +63 -0
- data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +98 -0
- data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +77 -0
- data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +149 -0
- data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +54 -0
- data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +32 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurence_incrementer.rb +794 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +132 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +61 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +34 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +50 -0
- data/lib/ri_cal/property_value/recurrence_rule/validations.rb +126 -0
- data/lib/ri_cal/property_value/recurrence_rule.rb +146 -0
- data/lib/ri_cal/property_value/text.rb +41 -0
- data/lib/ri_cal/property_value/uri.rb +12 -0
- data/lib/ri_cal/property_value/utc_offset.rb +34 -0
- data/lib/ri_cal/property_value.rb +110 -0
- data/lib/ri_cal/required_timezones.rb +56 -0
- data/lib/ri_cal/time_with_floating_timezone.rb +59 -0
- data/lib/ri_cal.rb +134 -0
- data/ri_cal.gemspec +47 -0
- data/sample_ical_files/from_ical_dot_app/test1.ics +38 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +71 -0
- data/spec/ri_cal/component/alarm_spec.rb +13 -0
- data/spec/ri_cal/component/calendar_spec.rb +55 -0
- data/spec/ri_cal/component/event_spec.rb +157 -0
- data/spec/ri_cal/component/freebusy_spec.rb +13 -0
- data/spec/ri_cal/component/journal_spec.rb +13 -0
- data/spec/ri_cal/component/t_z_info_timezone_spec.rb +37 -0
- data/spec/ri_cal/component/timezone_spec.rb +155 -0
- data/spec/ri_cal/component/todo_spec.rb +61 -0
- data/spec/ri_cal/component_spec.rb +212 -0
- data/spec/ri_cal/core_extensions/time/calculations_spec.rb +189 -0
- data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +46 -0
- data/spec/ri_cal/occurrence_enumerator_spec.rb +218 -0
- data/spec/ri_cal/parser_spec.rb +304 -0
- data/spec/ri_cal/property_value/date_spec.rb +22 -0
- data/spec/ri_cal/property_value/date_time_spec.rb +448 -0
- data/spec/ri_cal/property_value/duration_spec.rb +80 -0
- data/spec/ri_cal/property_value/period_spec.rb +50 -0
- data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +22 -0
- data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1815 -0
- data/spec/ri_cal/property_value/text_spec.rb +14 -0
- data/spec/ri_cal/property_value/utc_offset_spec.rb +49 -0
- data/spec/ri_cal/property_value_spec.rb +111 -0
- data/spec/ri_cal/required_timezones_spec.rb +68 -0
- data/spec/ri_cal_spec.rb +54 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +24 -0
- data/tasks/ri_cal.rake +403 -0
- data/tasks/spec.rake +35 -0
- 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,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
|