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
data/docs/rfc2445.pdf
ADDED
Binary file
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. properties alarm.rb])
|
2
|
+
|
3
|
+
module RiCal
|
4
|
+
|
5
|
+
class Component
|
6
|
+
#- ©2009 Rick DeNatale
|
7
|
+
#- All rights reserved. Refer to the file README.txt for the license
|
8
|
+
#
|
9
|
+
# An Alarm component groups properties defining a reminder or alarm associated with an event or to-do
|
10
|
+
# TODO: The Alarm component has complex cardinality restrictions depending on the value of the action property
|
11
|
+
# i.e. audio, display, email, and proc alarms, this is currently not checked or enforced
|
12
|
+
#
|
13
|
+
# to see the property accessing methods for this class see the RiCal::Properties::Alarm module
|
14
|
+
class Alarm < Component
|
15
|
+
include RiCal::Properties::Alarm
|
16
|
+
|
17
|
+
def self.entity_name #:nodoc:
|
18
|
+
"VALARM"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. properties calendar.rb])
|
2
|
+
|
3
|
+
module RiCal
|
4
|
+
class Component
|
5
|
+
#- ©2009 Rick DeNatale
|
6
|
+
#- All rights reserved. Refer to the file README.txt for the license
|
7
|
+
#
|
8
|
+
# to see the property accessing methods for this class see the RiCal::Properties::Calendar module
|
9
|
+
class Calendar < Component
|
10
|
+
include RiCal::Properties::Calendar
|
11
|
+
attr_reader :tz_source #:nodoc:
|
12
|
+
|
13
|
+
def initialize(parent=nil, &init_block) #:nodoc:
|
14
|
+
super
|
15
|
+
@tz_source = 'TZINFO' # Until otherwise told
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.entity_name #:nodoc:
|
19
|
+
"VCALENDAR"
|
20
|
+
end
|
21
|
+
|
22
|
+
def tz_info_source? #:nodoc:
|
23
|
+
@tz_source == 'TZINFO'
|
24
|
+
end
|
25
|
+
|
26
|
+
def required_timezones # :nodoc:
|
27
|
+
@required_timezones ||= RequiredTimezones.new
|
28
|
+
end
|
29
|
+
|
30
|
+
def subcomponent_class # :nodoc:
|
31
|
+
{
|
32
|
+
:event => Event,
|
33
|
+
:todo => Todo,
|
34
|
+
:journal => Journal,
|
35
|
+
:freebusy => Freebusy,
|
36
|
+
:timezone => Timezone,
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def export_properties_to(export_stream) # :nodoc:
|
41
|
+
prodid_property.params["X-RICAL-TZSOURCE"] = @tz_source
|
42
|
+
export_prop_to(export_stream, "PRODID", prodid_property)
|
43
|
+
export_prop_to(export_stream, "CALSCALE", calscale_property)
|
44
|
+
export_prop_to(export_stream, "VERSION", version_property)
|
45
|
+
export_prop_to(export_stream, "METHOD", method_property)
|
46
|
+
end
|
47
|
+
|
48
|
+
def prodid_property_from_string(line) # :nodoc:
|
49
|
+
result = super
|
50
|
+
@tz_source = prodid_property.params["X-RICAL-TZSOURCE"]
|
51
|
+
result
|
52
|
+
end
|
53
|
+
|
54
|
+
# return an array of event components contained within this Calendar
|
55
|
+
def events
|
56
|
+
subcomponents["VEVENT"]
|
57
|
+
end
|
58
|
+
|
59
|
+
# add an event to the calendar
|
60
|
+
def add_subcomponent(component)
|
61
|
+
super(component)
|
62
|
+
component.add_date_times_to(required_timezones) if tz_info_source?
|
63
|
+
end
|
64
|
+
|
65
|
+
# return an array of todo components contained within this Calendar
|
66
|
+
def todos
|
67
|
+
subcomponents["VTODO"]
|
68
|
+
end
|
69
|
+
|
70
|
+
# return an array of journal components contained within this Calendar
|
71
|
+
def journals
|
72
|
+
subcomponents["VJOURNAL"]
|
73
|
+
end
|
74
|
+
|
75
|
+
# return an array of freebusy components contained within this Calendar
|
76
|
+
def freebusys
|
77
|
+
subcomponents["VFREEBUSY"]
|
78
|
+
end
|
79
|
+
|
80
|
+
class TimezoneID
|
81
|
+
attr_reader :identifier, :calendar
|
82
|
+
def initialize(identifier, calendar)
|
83
|
+
self.identifier, self.calendar = identifier, calendar
|
84
|
+
end
|
85
|
+
|
86
|
+
def tzinfo_timezone
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
|
90
|
+
def resolved
|
91
|
+
calendar.find_timezone(identifier)
|
92
|
+
end
|
93
|
+
|
94
|
+
def local_to_utc(local)
|
95
|
+
resolved.local_to_utc(date_time_prop)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# return an array of timezone components contained within this calendar
|
100
|
+
def timezones
|
101
|
+
subcomponents["VTIMEZONE"]
|
102
|
+
end
|
103
|
+
|
104
|
+
class TZInfoWrapper #:nodoc:
|
105
|
+
attr_reader :tzinfo, :calendar #:nodoc:
|
106
|
+
def initialize(tzinfo, calendar) #:nodoc:
|
107
|
+
@tzinfo = tzinfo
|
108
|
+
@calendar = calendar
|
109
|
+
end
|
110
|
+
|
111
|
+
def identifier #:nodoc:
|
112
|
+
tzinfo.identifier
|
113
|
+
end
|
114
|
+
|
115
|
+
def date_time(ruby_time, tzid) #:nodoc:
|
116
|
+
RiCal::PropertyValue::DateTime.new(calendar, :value => ruby_time, :params => {'TZID' => tzid})
|
117
|
+
end
|
118
|
+
|
119
|
+
def local_to_utc(utc) #:nodoc:
|
120
|
+
date_time(tzinfo.local_to_utc(utc.to_ri_cal_ruby_value), 'UTC')
|
121
|
+
end
|
122
|
+
|
123
|
+
def utc_to_local(local) #:nodoc:
|
124
|
+
date_time(tzinfo.utc_to_local(local.to_ri_cal_ruby_value), tzinfo.identifier)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def find_timezone(identifier) #:nodoc:
|
129
|
+
if tz_info_source?
|
130
|
+
begin
|
131
|
+
TZInfoWrapper.new(TZInfo::Timezone.get(identifier), self)
|
132
|
+
rescue ::TZInfo::InvalidTimezoneIdentifier => ex
|
133
|
+
raise RiCal::InvalidTimezoneIdentifier.invalid_tzinfo_identifier(identifier)
|
134
|
+
end
|
135
|
+
else
|
136
|
+
result = timezones.find {|tz| tz.tzid == identifier}
|
137
|
+
raise RiCal::InvalidTimezoneIdentifier.not_found_in_calendar(identifier) unless result
|
138
|
+
result
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def export_required_timezones(export_stream) # :nodoc:
|
143
|
+
required_timezones.export_to(export_stream)
|
144
|
+
end
|
145
|
+
|
146
|
+
class FoldingStream #:nodoc:
|
147
|
+
attr_reader :stream #:nodoc:
|
148
|
+
def initialize(stream) #:nodoc:
|
149
|
+
@stream = stream || StringIO.new
|
150
|
+
end
|
151
|
+
|
152
|
+
def string #:nodoc:
|
153
|
+
stream.string
|
154
|
+
end
|
155
|
+
|
156
|
+
def fold(string) #:nodoc:
|
157
|
+
stream.puts(string[0,73])
|
158
|
+
string = string[73..-1]
|
159
|
+
while string
|
160
|
+
stream.puts " #{string[0, 72]}"
|
161
|
+
string = string[72..-1]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def puts(*strings) #:nodoc:
|
166
|
+
strings.each do |string|
|
167
|
+
string.split("\n").each do |line|
|
168
|
+
fold(line)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Export this calendar as an iCalendar file.
|
175
|
+
# if to is nil (the default) then this method will return a string,
|
176
|
+
# otherwise to should be an IO to which the iCalendar file contents will be written
|
177
|
+
def export(to=nil)
|
178
|
+
export_stream = FoldingStream.new(to)
|
179
|
+
export_stream.puts("BEGIN:VCALENDAR")
|
180
|
+
#TODO: right now I'm assuming that all timezones are internal what happens when we export
|
181
|
+
# an imported calendar.
|
182
|
+
export_properties_to(export_stream)
|
183
|
+
export_x_properties_to(export_stream)
|
184
|
+
export_required_timezones(export_stream)
|
185
|
+
export_subcomponent_to(export_stream, events)
|
186
|
+
export_subcomponent_to(export_stream, todos)
|
187
|
+
export_subcomponent_to(export_stream, journals)
|
188
|
+
export_subcomponent_to(export_stream, freebusys)
|
189
|
+
export_stream.puts("END:VCALENDAR")
|
190
|
+
if to
|
191
|
+
nil
|
192
|
+
else
|
193
|
+
export_stream.string
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. properties event.rb])
|
2
|
+
|
3
|
+
module RiCal
|
4
|
+
class Component
|
5
|
+
#- ©2009 Rick DeNatale
|
6
|
+
#- All rights reserved. Refer to the file README.txt for the license
|
7
|
+
#
|
8
|
+
# An Event (VEVENT) calendar component groups properties describing a scheduled event.
|
9
|
+
# Events may have multiple occurrences
|
10
|
+
#
|
11
|
+
# Events may also contain one or more ALARM subcomponents
|
12
|
+
#
|
13
|
+
# to see the property accessing methods for this class see the RiCal::Properties::Event module
|
14
|
+
# to see the methods for enumerating occurrences of recurring events see the RiCal::OccurrenceEnumerator module
|
15
|
+
class Event < Component
|
16
|
+
include OccurrenceEnumerator
|
17
|
+
|
18
|
+
include RiCal::Properties::Event
|
19
|
+
|
20
|
+
def subcomponent_class #:nodoc:
|
21
|
+
{:alarm => Alarm }
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.entity_name #:nodoc:
|
25
|
+
"VEVENT"
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. properties freebusy.rb])
|
2
|
+
|
3
|
+
module RiCal
|
4
|
+
class Component
|
5
|
+
#- ©2009 Rick DeNatale
|
6
|
+
#- All rights reserved. Refer to the file README.txt for the license
|
7
|
+
#
|
8
|
+
# A Freebusy (VFREEBUSY) calendar component groups properties describing either a request for free/busy time,
|
9
|
+
# a response to a request for free/busy time, or a published set of busy time.
|
10
|
+
# to see the property accessing methods for this class see the RiCal::Properties::Freebusy module
|
11
|
+
class Freebusy < Component
|
12
|
+
include RiCal::Properties::Freebusy
|
13
|
+
|
14
|
+
def self.entity_name #:nodoc:
|
15
|
+
"VFREEBUSY"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
require File.join(File.dirname(__FILE__), %w[.. properties journal.rb])
|
3
|
+
|
4
|
+
module RiCal
|
5
|
+
class Component
|
6
|
+
#- ©2009 Rick DeNatale
|
7
|
+
#- All rights reserved. Refer to the file README.txt for the license
|
8
|
+
#
|
9
|
+
# A Journal (VJOURNAL) calendar component groups properties describing a journal entry.
|
10
|
+
# Journals may have multiple occurrences
|
11
|
+
# to see the property accessing methods for this class see the RiCal::Properties::Journal module
|
12
|
+
# to see the methods for enumerating occurrences of recurring journals see the RiCal::OccurrenceEnumerator module
|
13
|
+
class Journal < Component
|
14
|
+
include RiCal::Properties::Journal
|
15
|
+
include RiCal::OccurrenceEnumerator
|
16
|
+
|
17
|
+
def self.entity_name #:nodoc:
|
18
|
+
"VJOURNAL"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
#- ©2009 Rick DeNatale
|
2
|
+
#- All rights reserved. Refer to the file README.txt for the license
|
3
|
+
#
|
4
|
+
# A wrapper class for a Timezone implemented by the TZInfo Gem
|
5
|
+
# (or by Rails)
|
6
|
+
class RiCal::Component::TZInfoTimezone < RiCal::Component::Timezone
|
7
|
+
|
8
|
+
class Period #:nodoc: all
|
9
|
+
|
10
|
+
def initialize(which, this_period, prev_period)
|
11
|
+
@which = which
|
12
|
+
@onset = this_period.local_start.strftime("%Y%m%dT%H%M%S")
|
13
|
+
@offset_from = format_rfc2445_offset(prev_period.utc_total_offset)
|
14
|
+
@offset_to = format_rfc2445_offset(this_period.utc_total_offset)
|
15
|
+
@abbreviation = this_period.abbreviation
|
16
|
+
@rdates = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_period(this_period)
|
20
|
+
@rdates << this_period.local_start.strftime("%Y%m%dT%H%M%S")
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def format_rfc2445_offset(seconds) #:nodoc:
|
25
|
+
abs_seconds = seconds.abs
|
26
|
+
h = (abs_seconds/3600).floor
|
27
|
+
m = (abs_seconds - (h * 3600))/60
|
28
|
+
h *= -1 if seconds < 0
|
29
|
+
sprintf("%+03d%02d", h, m)
|
30
|
+
end
|
31
|
+
|
32
|
+
def export_to(export_stream)
|
33
|
+
export_stream.puts "BEGIN:#{@which}"
|
34
|
+
export_stream.puts "DTSTART:#{@onset}"
|
35
|
+
export_stream.puts "RDATE:#{@rdates.join(",")}"
|
36
|
+
export_stream.puts "TZOFFSETFROM:#{@offset_from}"
|
37
|
+
export_stream.puts "TZOFFSETTO:#{@offset_to}"
|
38
|
+
export_stream.puts "TZNAME:#{@abbreviation}"
|
39
|
+
export_stream.puts "END:#{@which}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Periods #:nodoc: all
|
44
|
+
|
45
|
+
def initialize
|
46
|
+
@dst_period = @std_period = @previous_period = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def daylight_period(this_period, previous_period)
|
50
|
+
@daylight_period ||= Period.new("DAYLIGHT", this_period, previous_period)
|
51
|
+
end
|
52
|
+
|
53
|
+
def standard_period(this_period, previous_period)
|
54
|
+
@standard_period ||= Period.new("STANDARD", this_period, previous_period)
|
55
|
+
end
|
56
|
+
|
57
|
+
def log_period(period)
|
58
|
+
@periods ||= []
|
59
|
+
@periods << period unless @periods.include?(period)
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_period(this_period)
|
63
|
+
if @previous_period
|
64
|
+
if this_period.dst?
|
65
|
+
period = daylight_period(this_period, @previous_period)
|
66
|
+
else
|
67
|
+
period = standard_period(this_period, @previous_period)
|
68
|
+
end
|
69
|
+
period.add_period(this_period)
|
70
|
+
log_period(period)
|
71
|
+
end
|
72
|
+
@previous_period = this_period
|
73
|
+
end
|
74
|
+
|
75
|
+
def export_to(export_stream)
|
76
|
+
@periods.each {|period| period.export_to(export_stream)}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
attr_reader :tzinfo_timezone #:nodoc:
|
81
|
+
|
82
|
+
def initialize(tzinfo_timezone) #:nodoc:
|
83
|
+
@tzinfo_timezone = tzinfo_timezone
|
84
|
+
end
|
85
|
+
|
86
|
+
# convert time from this time zone to utc time
|
87
|
+
def local_to_utc(time)
|
88
|
+
@tzinfo_timezone.local_to_utc(time.to_ri_cal_ruby_value)
|
89
|
+
end
|
90
|
+
|
91
|
+
# convert time from utc time to this time zone
|
92
|
+
def utc_to_local(time)
|
93
|
+
@tzinfo_timezone.utc_to_local(time.to_ri_cal_ruby_value)
|
94
|
+
end
|
95
|
+
|
96
|
+
# return the time zone identifier
|
97
|
+
def identifier
|
98
|
+
@tzinfo_timezone.identifier
|
99
|
+
end
|
100
|
+
|
101
|
+
def export_local_to(export_stream, local_start, local_end) #:nodoc:
|
102
|
+
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))
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_rfc2445_string(utc_start, utc_end) #:nodoc:
|
106
|
+
export_stream = StringIO.new
|
107
|
+
export_utc_to(export_stream, utc_start, utc_end)
|
108
|
+
export_stream.string
|
109
|
+
end
|
110
|
+
|
111
|
+
def export_utc_to(export_stream, utc_start, utc_end) #:nodoc:
|
112
|
+
export_stream.puts "BEGIN:VTIMEZONE","TZID;X-RICAL-TZSOURCE=TZINFO:#{identifier}"
|
113
|
+
periods = Periods.new
|
114
|
+
period = tzinfo_timezone.period_for_utc(utc_start)
|
115
|
+
#start with the period before the one containing utc_start
|
116
|
+
period = tzinfo_timezone.period_for_utc(period.utc_start - 1)
|
117
|
+
while period && period.utc_start < utc_end
|
118
|
+
periods.add_period(period)
|
119
|
+
period = tzinfo_timezone.period_for_utc(period.utc_end + 1)
|
120
|
+
end
|
121
|
+
periods.export_to(export_stream)
|
122
|
+
export_stream.puts "END:VTIMEZONE\n"
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RiCal
|
2
|
+
class Component
|
3
|
+
class Timezone
|
4
|
+
#- ©2009 Rick DeNatale
|
5
|
+
#- All rights reserved. Refer to the file README.txt for the license
|
6
|
+
#
|
7
|
+
# A DaylightPeriod is a TimezonePeriod during which daylight saving time *is* in effect
|
8
|
+
class DaylightPeriod < TimezonePeriod #:nodoc: all
|
9
|
+
|
10
|
+
def self.entity_name #:nodoc:
|
11
|
+
"DAYLIGHT"
|
12
|
+
end
|
13
|
+
|
14
|
+
def dst?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def swallows_local?(local, std_candidate)
|
19
|
+
([local.year, local.month, local.day] == [dtstart.year,dtstart.month, dtstart.day]) &&
|
20
|
+
local >= dtstart_property &&
|
21
|
+
local.advance(:seconds => (utc_total_offset - std_candidate.utc_total_offset)) < dtstart_property
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RiCal
|
2
|
+
class Component
|
3
|
+
class Timezone
|
4
|
+
#- ©2009 Rick DeNatale
|
5
|
+
#- All rights reserved. Refer to the file README.txt for the license
|
6
|
+
#
|
7
|
+
# A StandardPeriod is a TimezonePeriod during which daylight saving time is *not* in effect
|
8
|
+
class StandardPeriod < TimezonePeriod #:nodoc: all
|
9
|
+
|
10
|
+
def self.entity_name #:nodoc:
|
11
|
+
"STANDARD"
|
12
|
+
end
|
13
|
+
|
14
|
+
def dst?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def ambiguous_local?(time)
|
19
|
+
[time.year, time.month, time.day] == [dtstart.year, dtstart.month, dtstart.day]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w[.. .. properties timezone_period.rb])
|
2
|
+
|
3
|
+
module RiCal
|
4
|
+
class Component
|
5
|
+
class Timezone
|
6
|
+
#- ©2009 Rick DeNatale
|
7
|
+
#- All rights reserved. Refer to the file README.txt for the license
|
8
|
+
#
|
9
|
+
# A TimezonePeriod is a component of a timezone representing a period during which a particular offset from UTC is
|
10
|
+
# in effect.
|
11
|
+
#
|
12
|
+
# to see the property accessing methods for this class see the RiCal::Properties::TimezonePeriod module
|
13
|
+
class TimezonePeriod < Component
|
14
|
+
include Properties::TimezonePeriod
|
15
|
+
|
16
|
+
include OccurrenceEnumerator
|
17
|
+
|
18
|
+
def zone_identifier #:nodoc:
|
19
|
+
tzname.first
|
20
|
+
end
|
21
|
+
|
22
|
+
def dtend #:nodoc:
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def exdate_property #:nodoc:
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def utc_total_offset #:nodoc:
|
31
|
+
tzoffsetfrom_property.to_seconds
|
32
|
+
end
|
33
|
+
|
34
|
+
def exrule_property #:nodoc:
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def last_before_utc(utc_time) #:nodoc:
|
39
|
+
last_before_local(utc_time + tzoffsetfrom_property)
|
40
|
+
end
|
41
|
+
|
42
|
+
def last_before_local(local_time) #:nodoc:
|
43
|
+
cand_occurrence = nil
|
44
|
+
each do |occurrence|
|
45
|
+
return cand_occurrence if occurrence.dtstart_property > local_time
|
46
|
+
cand_occurrence = occurrence
|
47
|
+
end
|
48
|
+
return cand_occurrence
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|