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.
- checksums.yaml +7 -0
- data/History.txt +402 -0
- data/Manifest.txt +161 -0
- data/README.txt +410 -0
- data/Rakefile +69 -0
- data/VERSION +1 -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 +1 -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 +19 -0
- data/lib/ri_cal/component/calendar.rb +257 -0
- data/lib/ri_cal/component/event.rb +58 -0
- data/lib/ri_cal/component/freebusy.rb +16 -0
- data/lib/ri_cal/component/journal.rb +27 -0
- data/lib/ri_cal/component/non_standard.rb +33 -0
- data/lib/ri_cal/component/t_z_info_timezone.rb +153 -0
- data/lib/ri_cal/component/timezone/daylight_period.rb +25 -0
- data/lib/ri_cal/component/timezone/standard_period.rb +23 -0
- data/lib/ri_cal/component/timezone/timezone_period.rb +76 -0
- data/lib/ri_cal/component/timezone.rb +197 -0
- data/lib/ri_cal/component/todo.rb +42 -0
- data/lib/ri_cal/component.rb +256 -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 +56 -0
- data/lib/ri_cal/core_extensions/date.rb +13 -0
- data/lib/ri_cal/core_extensions/date_time/conversions.rb +50 -0
- data/lib/ri_cal/core_extensions/date_time.rb +15 -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 +57 -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 +42 -0
- data/lib/ri_cal/core_extensions/time/tzid_access.rb +50 -0
- data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +55 -0
- data/lib/ri_cal/core_extensions/time.rb +14 -0
- data/lib/ri_cal/core_extensions.rb +11 -0
- data/lib/ri_cal/fast_date_time.rb +234 -0
- data/lib/ri_cal/floating_timezone.rb +32 -0
- data/lib/ri_cal/invalid_property_value.rb +8 -0
- data/lib/ri_cal/invalid_timezone_identifier.rb +20 -0
- data/lib/ri_cal/occurrence_enumerator.rb +265 -0
- data/lib/ri_cal/occurrence_period.rb +17 -0
- data/lib/ri_cal/parser.rb +148 -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 +1523 -0
- data/lib/ri_cal/properties/freebusy.rb +593 -0
- data/lib/ri_cal/properties/journal.rb +1237 -0
- data/lib/ri_cal/properties/timezone.rb +150 -0
- data/lib/ri_cal/properties/timezone_period.rb +416 -0
- data/lib/ri_cal/properties/todo.rb +1559 -0
- data/lib/ri_cal/properties.rb +12 -0
- data/lib/ri_cal/property_value/array.rb +27 -0
- data/lib/ri_cal/property_value/cal_address.rb +11 -0
- data/lib/ri_cal/property_value/date.rb +184 -0
- data/lib/ri_cal/property_value/date_time/additive_methods.rb +44 -0
- data/lib/ri_cal/property_value/date_time/time_machine.rb +159 -0
- data/lib/ri_cal/property_value/date_time/timezone_support.rb +100 -0
- data/lib/ri_cal/property_value/date_time.rb +359 -0
- data/lib/ri_cal/property_value/duration.rb +110 -0
- data/lib/ri_cal/property_value/geo.rb +11 -0
- data/lib/ri_cal/property_value/integer.rb +12 -0
- data/lib/ri_cal/property_value/occurrence_list.rb +144 -0
- data/lib/ri_cal/property_value/period.rb +86 -0
- data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +100 -0
- data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +79 -0
- data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +148 -0
- data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +53 -0
- data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_day_incrementer.rb +86 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_hour_incrementer.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_minute_incrementer.rb +32 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_month_incrementer.rb +52 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_monthday_incrementer.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_numbered_day_incrementer.rb +38 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_second_incrementer.rb +32 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_weekno_incrementer.rb +69 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_yearday_incrementer.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/daily_incrementer.rb +28 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/frequency_incrementer.rb +80 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/hourly_incrementer.rb +23 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/list_incrementer.rb +106 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/minutely_incrementer.rb +23 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/monthly_incrementer.rb +33 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/null_sub_cycle_incrementer.rb +43 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/secondly_incrementer.rb +28 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/weekly_incrementer.rb +37 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/yearly_incrementer.rb +57 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer.rb +135 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +131 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +64 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +33 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +53 -0
- data/lib/ri_cal/property_value/recurrence_rule/time_manipulation.rb +42 -0
- data/lib/ri_cal/property_value/recurrence_rule/validations.rb +125 -0
- data/lib/ri_cal/property_value/recurrence_rule.rb +154 -0
- data/lib/ri_cal/property_value/text.rb +44 -0
- data/lib/ri_cal/property_value/uri.rb +11 -0
- data/lib/ri_cal/property_value/utc_offset.rb +33 -0
- data/lib/ri_cal/property_value/zulu_date_time.rb +34 -0
- data/lib/ri_cal/property_value.rb +159 -0
- data/lib/ri_cal/required_timezones.rb +55 -0
- data/lib/ri_cal.rb +187 -0
- data/parked_specs/ri_cal/claudio_a_bug_spec.rb +100 -0
- data/performance/empty_propval/subject.rb +43 -0
- data/performance/paris_eastern/subject.rb +90 -0
- data/performance/penultimate_weekday/subject.rb +15 -0
- data/performance/psm_big_enum/ical.ics +3171 -0
- data/performance/psm_big_enum/subject.rb +16 -0
- data/performance/utah_cycling/subject.rb +55 -0
- data/ri_cal.gemspec +244 -0
- data/script/benchmark_subject +23 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/profile_subject +29 -0
- data/script/txt2html +71 -0
- data/spec/ri_cal/bugreports_spec.rb +276 -0
- data/spec/ri_cal/component/alarm_spec.rb +12 -0
- data/spec/ri_cal/component/calendar_spec.rb +88 -0
- data/spec/ri_cal/component/event_spec.rb +735 -0
- data/spec/ri_cal/component/freebusy_spec.rb +12 -0
- data/spec/ri_cal/component/journal_spec.rb +37 -0
- data/spec/ri_cal/component/t_z_info_timezone_spec.rb +60 -0
- data/spec/ri_cal/component/timezone_spec.rb +236 -0
- data/spec/ri_cal/component/todo_spec.rb +112 -0
- data/spec/ri_cal/component_spec.rb +224 -0
- data/spec/ri_cal/core_extensions/string/conversions_spec.rb +78 -0
- data/spec/ri_cal/core_extensions/time/calculations_spec.rb +188 -0
- data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +45 -0
- data/spec/ri_cal/fast_date_time_spec.rb +77 -0
- data/spec/ri_cal/inf_loop_spec.rb +78 -0
- data/spec/ri_cal/occurrence_enumerator_spec.rb +611 -0
- data/spec/ri_cal/parser_spec.rb +337 -0
- data/spec/ri_cal/property_value/date_spec.rb +53 -0
- data/spec/ri_cal/property_value/date_time_spec.rb +383 -0
- data/spec/ri_cal/property_value/duration_spec.rb +126 -0
- data/spec/ri_cal/property_value/occurrence_list_spec.rb +72 -0
- data/spec/ri_cal/property_value/period_spec.rb +63 -0
- data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +21 -0
- data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1814 -0
- data/spec/ri_cal/property_value/text_spec.rb +25 -0
- data/spec/ri_cal/property_value/utc_offset_spec.rb +48 -0
- data/spec/ri_cal/property_value_spec.rb +125 -0
- data/spec/ri_cal/required_timezones_spec.rb +67 -0
- data/spec/ri_cal_spec.rb +53 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +50 -0
- data/tasks/gem_loader/load_active_support.rb +3 -0
- data/tasks/gem_loader/load_tzinfo_gem.rb +2 -0
- data/tasks/ri_cal.rake +412 -0
- data/tasks/spec.rake +102 -0
- metadata +246 -0
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
|