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,7 @@
1
+ Seconds - Freq="SECONDLY", BYSECOND
2
+ Minutes - Freq="MINUTELY", BYMINUTE
3
+ Hours - Freq="HOURLY", BYHOUR
4
+ Days - Freq="DAILY", BYDAY, BYMONTHDAY, BYYEARDAY
5
+ Weeks - Freq="WEEKLY", BYWEEKNO
6
+ Months - Freq="MONTHLY", BYMONTH
7
+ Years - Freq="YEARLY"
data/docs/rfc2445.pdf ADDED
Binary file
@@ -0,0 +1,19 @@
1
+ module RiCal
2
+
3
+ class Component
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # An Alarm component groups properties defining a reminder or alarm associated with an event or to-do
7
+ # TODO: The Alarm component has complex cardinality restrictions depending on the value of the action property
8
+ # i.e. audio, display, email, and proc alarms, this is currently not checked or enforced
9
+ #
10
+ # to see the property accessing methods for this class see the RiCal::Properties::Alarm module
11
+ class Alarm < Component
12
+ include RiCal::Properties::Alarm
13
+
14
+ def self.entity_name #:nodoc:
15
+ "VALARM"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,257 @@
1
+ module RiCal
2
+ class Component
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # to see the property accessing methods for this class see the RiCal::Properties::Calendar module
6
+ class Calendar < Component
7
+ include RiCal::Properties::Calendar
8
+ attr_reader :tz_source #:nodoc:
9
+
10
+ def initialize(parent=nil,entity_name = nil, &init_block) #:nodoc:
11
+ @tz_source = 'TZINFO' # Until otherwise told
12
+ super
13
+ end
14
+
15
+ def self.entity_name #:nodoc:
16
+ "VCALENDAR"
17
+ end
18
+
19
+ def tz_info_source? #:nodoc:
20
+ @tz_source == 'TZINFO'
21
+ end
22
+
23
+ def required_timezones # :nodoc:
24
+ @required_timezones ||= RequiredTimezones.new
25
+ end
26
+
27
+ def subcomponent_class # :nodoc:
28
+ {
29
+ :event => Event,
30
+ :todo => Todo,
31
+ :journal => Journal,
32
+ :freebusy => Freebusy,
33
+ :timezone => Timezone,
34
+ }
35
+ end
36
+
37
+ def export_properties_to(export_stream) # :nodoc:
38
+ prodid_property.params["X-RICAL-TZSOURCE"] = @tz_source if @tz_source
39
+ export_prop_to(export_stream, "PRODID", prodid_property)
40
+ export_prop_to(export_stream, "CALSCALE", calscale_property)
41
+ export_prop_to(export_stream, "VERSION", version_property)
42
+ export_prop_to(export_stream, "METHOD", method_property)
43
+ end
44
+
45
+ def prodid_property_from_string(line) # :nodoc:
46
+ result = super
47
+ @tz_source = prodid_property.params["X-RICAL-TZSOURCE"]
48
+ result
49
+ end
50
+
51
+ # Return the default time zone identifier for this calendar
52
+ def default_tzid
53
+ @default_tzid || PropertyValue::DateTime.default_tzid
54
+ end
55
+
56
+ # Set the default time zone identifier for this calendar
57
+ # To set the default to floating times use a value of :floating
58
+ def default_tzid=(value)
59
+ @default_tzid=value
60
+ end
61
+
62
+ # return an array of event components contained within this Calendar
63
+ def events
64
+ subcomponents["VEVENT"]
65
+ end
66
+
67
+ # add an event to the calendar
68
+ def add_subcomponent(component)
69
+ super(component)
70
+ component.add_date_times_to(required_timezones) if tz_info_source?
71
+ end
72
+
73
+ # return an array of todo components contained within this Calendar
74
+ def todos
75
+ subcomponents["VTODO"]
76
+ end
77
+
78
+ # return an array of journal components contained within this Calendar
79
+ def journals
80
+ subcomponents["VJOURNAL"]
81
+ end
82
+
83
+ # return an array of freebusy components contained within this Calendar
84
+ def freebusys
85
+ subcomponents["VFREEBUSY"]
86
+ end
87
+
88
+ class TimezoneID #:nodoc:
89
+ attr_reader :identifier, :calendar
90
+ def initialize(identifier, calendar)
91
+ self.identifier, self.calendar = identifier, calendar
92
+ end
93
+
94
+ def tzinfo_timezone
95
+ nil
96
+ end
97
+
98
+ def resolved
99
+ calendar.find_timezone(identifier)
100
+ end
101
+
102
+ def local_to_utc(local)
103
+ resolved.local_to_utc(date_time_prop)
104
+ end
105
+ end
106
+
107
+ # return an array of timezone components contained within this calendar
108
+ def timezones
109
+ subcomponents["VTIMEZONE"]
110
+ end
111
+
112
+ class TZInfoWrapper #:nodoc:
113
+ attr_reader :tzinfo, :calendar #:nodoc:
114
+ def initialize(tzinfo, calendar) #:nodoc:
115
+ @tzinfo = tzinfo
116
+ @calendar = calendar
117
+ end
118
+
119
+ def identifier #:nodoc:
120
+ tzinfo.identifier
121
+ end
122
+
123
+ def local_date_time(ruby_time, tzid) #:nodoc:
124
+ RiCal::PropertyValue::DateTime.new(calendar, :value => ruby_time.strftime("%Y%m%dT%H%M%S"), :params => {'TZID' => tzid})
125
+ end
126
+
127
+ def utc_date_time(ruby_time) #:nodoc
128
+ RiCal::PropertyValue::DateTime.new(calendar, :value => ruby_time.strftime("%Y%m%dT%H%M%SZ"))
129
+ end
130
+
131
+ def local_to_utc(utc) #:nodoc:
132
+ utc_date_time(tzinfo.local_to_utc(utc.to_ri_cal_ruby_value))
133
+ end
134
+
135
+ def utc_to_local(local) #:nodoc:
136
+ local_date_time(tzinfo.utc_to_local(local.to_ri_cal_ruby_value), tzinfo.identifier)
137
+ end
138
+
139
+
140
+ def rational_utc_offset(local)
141
+ RiCal.RationalOffset[tzinfo.period_for_local(local, true).utc_total_offset]
142
+ end
143
+
144
+ end
145
+
146
+ def find_timezone(identifier) #:nodoc:
147
+ if tz_info_source?
148
+ begin
149
+ TZInfoWrapper.new(TZInfo::Timezone.get(identifier), self)
150
+ rescue ::TZInfo::InvalidTimezoneIdentifier => ex
151
+ raise RiCal::InvalidTimezoneIdentifier.invalid_tzinfo_identifier(identifier)
152
+ end
153
+ else
154
+ result = timezones.find {|tz| tz.tzid == identifier}
155
+ raise RiCal::InvalidTimezoneIdentifier.not_found_in_calendar(identifier) unless result
156
+ result
157
+ end
158
+ end
159
+
160
+ def export_required_timezones(export_stream) # :nodoc:
161
+ required_timezones.export_to(export_stream)
162
+ end
163
+
164
+ class FoldingStream #:nodoc:
165
+ attr_reader :stream #:nodoc:
166
+ def initialize(stream) #:nodoc:
167
+ @stream = stream || StringIO.new
168
+ end
169
+
170
+ def string #:nodoc:
171
+ stream.string
172
+ end
173
+
174
+ if RUBY_VERSION =~ /^1\.9/
175
+ def utf8_safe_split(string, n)
176
+ if string.bytesize <= n
177
+ [string, nil]
178
+ else
179
+ bytes = string.bytes.to_a
180
+ while (128..191).include?(bytes[n])
181
+ n = n - 1
182
+ end
183
+ before = bytes[0,n]
184
+ after = bytes[n..-1]
185
+ [before.pack("C*").force_encoding("utf-8"), after.empty? ? nil : after.pack("C*").force_encoding("utf-8")]
186
+ end
187
+ end
188
+ else
189
+ def valid_utf8?(string)
190
+ string.unpack("U") rescue nil
191
+ end
192
+
193
+ def utf8_safe_split(string, n)
194
+ if string.length <= n
195
+ [string, nil]
196
+ else
197
+ before = string[0, n]
198
+ after = string[n..-1]
199
+ until valid_utf8?(after)
200
+ n = n - 1
201
+ before = string[0, n]
202
+ after = string[n..-1]
203
+ end
204
+ [before, after.empty? ? nil : after]
205
+ end
206
+ end
207
+ end
208
+
209
+ def fold(string) #:nodoc:
210
+ line, remainder = *utf8_safe_split(string, 73)
211
+ stream.puts(line)
212
+ while remainder
213
+ line, remainder = *utf8_safe_split(remainder, 72)
214
+ stream.puts(" #{line}")
215
+ end
216
+ end
217
+
218
+ def puts(*strings) #:nodoc:
219
+ strings.each do |string|
220
+ string.split("\n").each do |line|
221
+ fold(line)
222
+ end
223
+ end
224
+ end
225
+ end
226
+
227
+ # Export this calendar as an iCalendar file.
228
+ # if to is nil (the default) then this method will return a string,
229
+ # otherwise to should be an IO to which the iCalendar file contents will be written
230
+ def export(to=nil)
231
+ export_stream = FoldingStream.new(to)
232
+ export_stream.puts("BEGIN:VCALENDAR")
233
+ export_properties_to(export_stream)
234
+ export_x_properties_to(export_stream)
235
+ export_required_timezones(export_stream)
236
+ export_subcomponent_to(export_stream, events)
237
+ export_subcomponent_to(export_stream, todos)
238
+ export_subcomponent_to(export_stream, journals)
239
+ export_subcomponent_to(export_stream, freebusys)
240
+ subcomponents.each do |key, value|
241
+ unless %{VEVENT VTODO VJOURNAL VFREEBUSYS}.include?(key)
242
+ export_subcomponent_to(export_stream, value)
243
+ end
244
+ end
245
+ export_stream.puts("END:VCALENDAR")
246
+ if to
247
+ nil
248
+ else
249
+ export_stream.string
250
+ end
251
+ end
252
+
253
+ alias_method :export_to, :export
254
+
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,58 @@
1
+ module RiCal
2
+ class Component
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # An Event (VEVENT) calendar component groups properties describing a scheduled event.
6
+ # Events may have multiple occurrences
7
+ #
8
+ # Events may also contain one or more ALARM subcomponents
9
+ #
10
+ # to see the property accessing methods for this class see the RiCal::Properties::Event module
11
+ # to see the methods for enumerating occurrences of recurring events see the RiCal::OccurrenceEnumerator module
12
+ class Event < Component
13
+ include OccurrenceEnumerator
14
+
15
+ include RiCal::Properties::Event
16
+
17
+ def subcomponent_class #:nodoc:
18
+ {:alarm => Alarm }
19
+ end
20
+
21
+ def self.entity_name #:nodoc:
22
+ "VEVENT"
23
+ end
24
+
25
+ # Return a date_time representing the time at which the event starts
26
+ def start_time
27
+ dtstart_property ? dtstart.to_datetime : nil
28
+ end
29
+
30
+ # Return a date_time_property representing the time at which the event ends
31
+ def finish_property
32
+ if dtend_property
33
+ dtend_property
34
+ elsif duration_property
35
+ (dtstart_property + duration_property)
36
+ else
37
+ dtstart_property
38
+ end
39
+ end
40
+
41
+ # Return a date_time representing the time at which the event starts
42
+ def finish_time
43
+ prop = finish_property
44
+ prop ? prop.to_finish_time : nil
45
+ end
46
+
47
+ def zulu_occurrence_range_start_time
48
+ dtstart_property ? dtstart_property.to_zulu_occurrence_range_start_time : nil
49
+ end
50
+
51
+ def zulu_occurrence_range_finish_time
52
+ prop = finish_property
53
+ prop ? prop.to_zulu_occurrence_range_finish_time : nil
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,16 @@
1
+ module RiCal
2
+ class Component
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # A Freebusy (VFREEBUSY) calendar component groups properties describing either a request for free/busy time,
6
+ # a response to a request for free/busy time, or a published set of busy time.
7
+ # to see the property accessing methods for this class see the RiCal::Properties::Freebusy module
8
+ class Freebusy < Component
9
+ include RiCal::Properties::Freebusy
10
+
11
+ def self.entity_name #:nodoc:
12
+ "VFREEBUSY"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ module RiCal
2
+ class Component
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # A Journal (VJOURNAL) calendar component groups properties describing a journal entry.
6
+ # Journals may have multiple occurrences
7
+ # to see the property accessing methods for this class see the RiCal::Properties::Journal module
8
+ # to see the methods for enumerating occurrences of recurring journals see the RiCal::OccurrenceEnumerator module
9
+ class Journal < Component
10
+ include RiCal::Properties::Journal
11
+ include RiCal::OccurrenceEnumerator
12
+
13
+ def self.entity_name #:nodoc:
14
+ "VJOURNAL"
15
+ end
16
+
17
+ # Return a date_time representing the time at which the event starts
18
+ def start_time
19
+ dtstart.to_datetime
20
+ end
21
+
22
+ # Journals take up no calendar time, so the finish time is always the same as the start_time
23
+ alias_method :finish_time, :start_time
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ module RiCal
2
+
3
+ class Component
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # An NonStandard component represents a component (or subcomponent) not listed in RFC2445.
7
+ # For example some icalendar data contains VVENUE components, a proposed extension to RFC2445
8
+ # which was dropped.
9
+ class NonStandard < Component
10
+ attr_reader :entity_name
11
+
12
+ def initialize(parent, entity_name)
13
+ super(parent)
14
+ @entity_name = entity_name
15
+ @source_lines = []
16
+ end
17
+
18
+ def process_line(parser, line) #:nodoc:
19
+ if line[:name] == "BEGIN"
20
+ parse_subcomponent(parser, line)
21
+ else
22
+ @source_lines << parser.last_line_str
23
+ end
24
+ end
25
+
26
+ def export_properties_to(stream)
27
+ @source_lines.each do |line|
28
+ stream.puts(line)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,153 @@
1
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
2
+ #
3
+ # A wrapper class for a Timezone implemented by the TZInfo Gem
4
+ # (or by Rails)
5
+ #
6
+
7
+ class RiCal::Component::TZInfoTimezone < RiCal::Component::Timezone
8
+
9
+ class Period #:nodoc: all
10
+
11
+ def initialize(which, this_period, prev_period)
12
+ @which = which
13
+ if prev_period
14
+ @onset = period_local_end(prev_period)
15
+ @offset_from = format_rfc2445_offset(prev_period.utc_total_offset)
16
+ else
17
+ @onset = period_local_start(this_period)
18
+ @offset_from = format_rfc2445_offset(this_period.utc_total_offset)
19
+ end
20
+ @offset_to = format_rfc2445_offset(this_period.utc_total_offset)
21
+ @abbreviation = this_period.abbreviation
22
+ @rdates = []
23
+ end
24
+
25
+ def daylight?
26
+ @which == "DAYLIGHT"
27
+ end
28
+
29
+ def period_local_end(period)
30
+ (period.local_end || DateTime.parse("99990101T000000")).strftime("%Y%m%dT%H%M%S")
31
+ end
32
+
33
+ # This assumes a 1 hour shift which is why we use the previous period local end when
34
+ # possible
35
+ def period_local_start(period)
36
+ shift = daylight? ? Rational(-1, 24) : Rational(1, 24)
37
+ ((period.local_start || DateTime.parse("16010101T000000")) + shift).strftime("%Y%m%dT%H%M%S")
38
+ end
39
+
40
+ def add_period(this_period)
41
+ @rdates << period_local_start(this_period)
42
+ end
43
+
44
+
45
+ def format_rfc2445_offset(seconds) #:nodoc:
46
+ abs_seconds = seconds.abs
47
+ h = (abs_seconds/3600).floor
48
+ m = (abs_seconds - (h * 3600))/60
49
+ h *= -1 if seconds < 0
50
+ sprintf("%+03d%02d", h, m)
51
+ end
52
+
53
+ def export_to(export_stream)
54
+ export_stream.puts "BEGIN:#{@which}"
55
+ export_stream.puts "DTSTART:#{@onset}"
56
+ @rdates.each do |rdate|
57
+ export_stream.puts "RDATE:#{rdate}"
58
+ end
59
+ export_stream.puts "TZOFFSETFROM:#{@offset_from}"
60
+ export_stream.puts "TZOFFSETTO:#{@offset_to}"
61
+ export_stream.puts "TZNAME:#{@abbreviation}"
62
+ export_stream.puts "END:#{@which}"
63
+ end
64
+ end
65
+
66
+ class Periods #:nodoc: all
67
+
68
+ def initialize
69
+ @dst_period = @std_period = @previous_period = nil
70
+ end
71
+
72
+ def empty?
73
+ @periods.nil? || @periods.empty?
74
+ end
75
+
76
+ def daylight_period(this_period, previous_period)
77
+ @daylight_period ||= Period.new("DAYLIGHT", this_period, previous_period)
78
+ end
79
+
80
+ def standard_period(this_period, previous_period)
81
+ @standard_period ||= Period.new("STANDARD", this_period, previous_period)
82
+ end
83
+
84
+ def log_period(period)
85
+ @periods ||= []
86
+ @periods << period unless @periods.include?(period)
87
+ end
88
+
89
+ def add_period(this_period, force=false)
90
+ if @previous_period || force
91
+ if this_period.dst?
92
+ period = daylight_period(this_period, @previous_period)
93
+ else
94
+ period = standard_period(this_period, @previous_period)
95
+ end
96
+ period.add_period(this_period)
97
+ log_period(period)
98
+ end
99
+ @previous_period = this_period
100
+ end
101
+
102
+ def export_to(export_stream)
103
+ @periods.each {|period| period.export_to(export_stream)}
104
+ end
105
+ end
106
+
107
+ attr_reader :tzinfo_timezone #:nodoc:
108
+
109
+ def initialize(tzinfo_timezone) #:nodoc:
110
+ @tzinfo_timezone = tzinfo_timezone
111
+ end
112
+
113
+ # convert time from this time zone to utc time
114
+ def local_to_utc(time)
115
+ @tzinfo_timezone.local_to_utc(time.to_ri_cal_ruby_value)
116
+ end
117
+
118
+ # convert time from utc time to this time zone
119
+ def utc_to_local(time)
120
+ @tzinfo_timezone.utc_to_local(time.to_ri_cal_ruby_value)
121
+ end
122
+
123
+ # return the time zone identifier
124
+ def identifier
125
+ @tzinfo_timezone.identifier
126
+ end
127
+
128
+ def export_local_to(export_stream, local_start, local_end) #:nodoc:
129
+ export_utc_to(export_stream, local_to_utc(local_start.to_ri_cal_ruby_value), local_to_utc(local_end.to_ri_cal_ruby_value))
130
+ end
131
+
132
+ def to_rfc2445_string(utc_start, utc_end) #:nodoc:
133
+ export_stream = StringIO.new
134
+ export_utc_to(export_stream, utc_start, utc_end)
135
+ export_stream.string
136
+ end
137
+
138
+ def export_utc_to(export_stream, utc_start, utc_end) #:nodoc:
139
+ export_stream.puts "BEGIN:VTIMEZONE","TZID;X-RICAL-TZSOURCE=TZINFO:#{identifier}"
140
+ periods = Periods.new
141
+ period = initial_period = tzinfo_timezone.period_for_utc(utc_start)
142
+ #start with the period before the one containing utc_start
143
+ prev_period = period.utc_start && tzinfo_timezone.period_for_utc(period.utc_start - 1)
144
+ period = prev_period if prev_period
145
+ while period && period.utc_start && period.utc_start < utc_end
146
+ periods.add_period(period)
147
+ period = period.utc_end && tzinfo_timezone.period_for_utc(period.utc_end + 1)
148
+ end
149
+ periods.add_period(initial_period, :force) if periods.empty?
150
+ periods.export_to(export_stream)
151
+ export_stream.puts "END:VTIMEZONE\n"
152
+ end
153
+ end
@@ -0,0 +1,25 @@
1
+ module RiCal
2
+ class Component
3
+ class Timezone
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # A DaylightPeriod is a TimezonePeriod during which daylight saving time *is* in effect
7
+ class DaylightPeriod < TimezonePeriod #:nodoc: all
8
+
9
+ def self.entity_name #:nodoc:
10
+ "DAYLIGHT"
11
+ end
12
+
13
+ def dst?
14
+ true
15
+ end
16
+
17
+ def swallows_local?(local, std_candidate)
18
+ ([local.year, local.month, local.day] == [dtstart.year,dtstart.month, dtstart.day]) &&
19
+ local >= dtstart_property &&
20
+ local.advance(:seconds => (std_candidate.utc_total_offset - utc_total_offset)) < dtstart_property
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ module RiCal
2
+ class Component
3
+ class Timezone
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # A StandardPeriod is a TimezonePeriod during which daylight saving time is *not* in effect
7
+ class StandardPeriod < TimezonePeriod #:nodoc: all
8
+
9
+ def self.entity_name #:nodoc:
10
+ "STANDARD"
11
+ end
12
+
13
+ def dst?
14
+ false
15
+ end
16
+
17
+ def ambiguous_local?(time)
18
+ [time.year, time.month, time.day] == [dtstart.year, dtstart.month, dtstart.day]
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end