icalendar 1.0.2 → 1.1.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.
- data/README +125 -0
- data/Rakefile +3 -3
- data/docs/api/STUB +0 -0
- data/lib/icalendar.rb +2 -0
- data/lib/icalendar/calendar.rb +10 -1
- data/lib/icalendar/component.rb +12 -8
- data/lib/icalendar/component/alarm.rb +1 -1
- data/lib/icalendar/component/event.rb +7 -1
- data/lib/icalendar/component/timezone.rb +21 -3
- data/lib/icalendar/conversions.rb +13 -2
- data/lib/icalendar/parser.rb +118 -3
- data/lib/icalendar/tzinfo.rb +121 -0
- data/test/component/event_test.rb +212 -0
- data/test/component/timezone_test.rb +67 -0
- data/test/conversions_test.rb +2 -2
- data/test/coverage/STUB +0 -0
- metadata +71 -60
data/README
CHANGED
@@ -75,6 +75,131 @@ Now for some examples:
|
|
75
75
|
# network port, database etc.
|
76
76
|
cal_string = cal.to_ical
|
77
77
|
puts cal_string
|
78
|
+
|
79
|
+
## ALARMS
|
80
|
+
|
81
|
+
## Within an event, you can create e-mail notification alarms like this...
|
82
|
+
|
83
|
+
cal.event.do
|
84
|
+
# ...other event properties
|
85
|
+
alarm do
|
86
|
+
action "EMAIL"
|
87
|
+
description "This is an event reminder" # email body (required)
|
88
|
+
summary "Alarm notification" # email subject (required)
|
89
|
+
attendees %w(mailto:me@my-domain.com mailto:me-too@my-domain.com) # one or more email recipients (required)
|
90
|
+
add_attendee "mailto:me-three@my-domain.com"
|
91
|
+
remove_attendee "mailto:me@my-domain.com"
|
92
|
+
trigger "-PT15M" # 15 minutes before
|
93
|
+
add_attach "ftp://host.com/novo-procs/felizano.exe", {"FMTTYPE" => "application/binary"} # email attachments (optional)
|
94
|
+
end
|
95
|
+
|
96
|
+
alarm do
|
97
|
+
action "DISPLAY" # This line isn't necessary, it's the default
|
98
|
+
summary "Alarm notification"
|
99
|
+
trigger "-P1DT0H0M0S" # 1 day before
|
100
|
+
end
|
101
|
+
|
102
|
+
alarm do
|
103
|
+
action "AUDIO"
|
104
|
+
trigger "-PT15M"
|
105
|
+
add_attach "Basso", {"VALUE" => ["URI"]} # only one attach allowed (optional)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Output
|
110
|
+
|
111
|
+
# BEGIN:VALARM
|
112
|
+
# ACTION:EMAIL
|
113
|
+
# ATTACH;FMTTYPE=application/binary:ftp://host.com/novo-procs/felizano.exe
|
114
|
+
# TRIGGER:-PT15M
|
115
|
+
# SUMMARY:Alarm notification
|
116
|
+
# DESCRIPTION:This is an event reminder
|
117
|
+
# ATTENDEE:mailto:me-too@my-domain.com
|
118
|
+
# ATTENDEE:mailto:me-three@my-domain.com
|
119
|
+
# END:VALARM
|
120
|
+
#
|
121
|
+
# BEGIN:VALARM
|
122
|
+
# ACTION:DISPLAY
|
123
|
+
# TRIGGER:-P1DT0H0M0S
|
124
|
+
# SUMMARY:Alarm notification
|
125
|
+
# END:VALARM
|
126
|
+
#
|
127
|
+
# BEGIN:VALARM
|
128
|
+
# ACTION:AUDIO
|
129
|
+
# ATTACH;VALUE=URI:Basso
|
130
|
+
# TRIGGER:-PT15M
|
131
|
+
# END:VALARM
|
132
|
+
|
133
|
+
## Timezones
|
134
|
+
|
135
|
+
# Create a timezone definition (previous convention)
|
136
|
+
cal = Calendar.new
|
137
|
+
timezone = Icalendar::Timezone.new
|
138
|
+
daylight = Icalendar::Daylight.new
|
139
|
+
standard = Icalendar::Standard.new
|
140
|
+
|
141
|
+
timezone.timezone_id = "America/Chicago"
|
142
|
+
|
143
|
+
daylight.timezone_offset_from = "-0600"
|
144
|
+
daylight.timezone_offset_to = "-0500"
|
145
|
+
daylight.timezone_name = "CDT"
|
146
|
+
daylight.dtstart = "19700308TO20000"
|
147
|
+
daylight.recurrence_rules = ["FREQ=YEARLY;BYMONTH=3;BYDAY=2SU"]
|
148
|
+
|
149
|
+
standard.timezone_offset_from = "-0500"
|
150
|
+
standard.timezone_offset_to = "-0600"
|
151
|
+
standard.timezone_name = "CST"
|
152
|
+
standard.dtstart = "19701101T020000"
|
153
|
+
standard.recurrence_rules = ["YEARLY;BYMONTH=11;BYDAY=1SU"]
|
154
|
+
|
155
|
+
timezone.add(daylight)
|
156
|
+
timezone.add(standard)
|
157
|
+
cal.add(timezone)
|
158
|
+
|
159
|
+
# Now, you can make timezones like this
|
160
|
+
cal = Calendar.new
|
161
|
+
cal.timezone do
|
162
|
+
timezone_id "America/Chicago"
|
163
|
+
|
164
|
+
daylight do
|
165
|
+
timezone_offset_from "-0600"
|
166
|
+
timezone_offset_to "-0500"
|
167
|
+
timezone_name "CDT"
|
168
|
+
dtstart "19700308TO20000"
|
169
|
+
add_recurrence_rule "FREQ=YEARLY;BYMONTH=3;BYDAY=2SU"
|
170
|
+
end
|
171
|
+
|
172
|
+
standard do
|
173
|
+
timezone_offset_from "-0500"
|
174
|
+
timezone_offset_to "-0600"
|
175
|
+
timezone_name "CST"
|
176
|
+
dtstart "19701101T020000"
|
177
|
+
add_recurrence_rule "YEARLY;BYMONTH=11;BYDAY=1SU"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Both conventions output
|
182
|
+
|
183
|
+
# BEGIN:VTIMEZONE
|
184
|
+
# TZID:America/Chicago
|
185
|
+
# BEGIN:DAYLIGHT
|
186
|
+
# TZOFFSETFROM:-0600
|
187
|
+
# TZOFFSETTO:-0500
|
188
|
+
# TZNAME:CDT
|
189
|
+
# DTSTART:19700308T020000
|
190
|
+
# RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
|
191
|
+
# END:DAYLIGHT
|
192
|
+
# BEGIN:STANDARD
|
193
|
+
# TZOFFSETFROM:-0500
|
194
|
+
# TZOFFSETTO:-0600
|
195
|
+
# TZNAME:CST
|
196
|
+
# DTSTART:19701101T020000
|
197
|
+
# RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
|
198
|
+
# END:STANDARD
|
199
|
+
# END:VTIMEZONE
|
200
|
+
|
201
|
+
== Unicode
|
202
|
+
Add `$KCODE = 'u'` to make icalender work correctly with Utf8 texts
|
78
203
|
|
79
204
|
== Parsing iCalendars:
|
80
205
|
|
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ require 'rake/rdoctask'
|
|
5
5
|
require 'rake/clean'
|
6
6
|
require 'rake/contrib/sshpublisher'
|
7
7
|
|
8
|
-
PKG_VERSION = "1.0
|
8
|
+
PKG_VERSION = "1.1.0"
|
9
9
|
|
10
10
|
$VERBOSE = nil
|
11
11
|
TEST_CHANGES_SINCE = Time.now - 600 # Recent tests = changed in last 10 minutes
|
@@ -74,8 +74,8 @@ spec = Gem::Specification.new do |s|
|
|
74
74
|
s.extra_rdoc_files = ["README", "COPYING", "GPL"]
|
75
75
|
s.rdoc_options.concat ['--main', 'README']
|
76
76
|
|
77
|
-
s.author = "
|
78
|
-
s.email = "
|
77
|
+
s.author = "Sean Dague"
|
78
|
+
s.email = "sean@dague.net"
|
79
79
|
end
|
80
80
|
|
81
81
|
Rake::GemPackageTask.new(spec) do |pkg|
|
data/docs/api/STUB
ADDED
File without changes
|
data/lib/icalendar.rb
CHANGED
data/lib/icalendar/calendar.rb
CHANGED
@@ -27,9 +27,18 @@ module Icalendar
|
|
27
27
|
|
28
28
|
def event(&block)
|
29
29
|
e = Event.new
|
30
|
+
# Note: I'm not sure this is the best way to pass this down, but it works
|
31
|
+
e.tzid = self.timezones[0].tzid if self.timezones.length > 0
|
32
|
+
|
30
33
|
self.add_component e
|
31
34
|
|
32
|
-
|
35
|
+
if block
|
36
|
+
e.instance_eval &block
|
37
|
+
if e.tzid
|
38
|
+
e.dtstart.ical_params = { "TZID" => e.tzid }
|
39
|
+
e.dtend.ical_params = { "TZID" => e.tzid }
|
40
|
+
end
|
41
|
+
end
|
33
42
|
|
34
43
|
e
|
35
44
|
end
|
data/lib/icalendar/component.rb
CHANGED
@@ -145,25 +145,29 @@ module Icalendar
|
|
145
145
|
|
146
146
|
# Property value
|
147
147
|
value = ":#{val.to_ical}"
|
148
|
-
|
149
|
-
s << escaped.slice!(0, MAX_LINE_LENGTH) << "\r\n " while escaped.size > MAX_LINE_LENGTH
|
150
|
-
s << escaped << "\r\n"
|
151
|
-
s.gsub!(/ *$/, '')
|
148
|
+
add_sliced_text(s,prelude+escape_chars(value))
|
152
149
|
else
|
153
150
|
prelude = "#{key.gsub(/_/, '-').upcase}"
|
154
151
|
val.each do |v|
|
155
152
|
params = print_parameters(v)
|
156
153
|
value = ":#{v.to_ical}"
|
157
|
-
|
158
|
-
s << escaped.slice!(0, MAX_LINE_LENGTH) << "\r\n " while escaped.size > MAX_LINE_LENGTH
|
159
|
-
s << escaped << "\r\n"
|
160
|
-
s.gsub!(/ *$/, '')
|
154
|
+
add_sliced_text(s,prelude + params + escape_chars(value))
|
161
155
|
end
|
162
156
|
end
|
163
157
|
end
|
164
158
|
s
|
165
159
|
end
|
166
160
|
|
161
|
+
def escape_chars(value)
|
162
|
+
value.gsub("\\", "\\\\").gsub("\n", "\\n").gsub(",", "\\,").gsub(";", "\\;")
|
163
|
+
end
|
164
|
+
|
165
|
+
def add_sliced_text(add_to,escaped)
|
166
|
+
escaped = escaped.split('') # split is unicdoe-aware when `$KCODE = 'u'`
|
167
|
+
add_to << escaped.slice!(0,MAX_LINE_LENGTH).to_s << "\r\n " while escaped.length != 0 # shift(MAX_LINE_LENGTH) does not work with ruby 1.8.6
|
168
|
+
add_to.gsub!(/ *$/, '')
|
169
|
+
end
|
170
|
+
|
167
171
|
# Print the parameters for a specific property
|
168
172
|
def print_parameters(val)
|
169
173
|
s = ""
|
@@ -31,7 +31,7 @@ module Icalendar
|
|
31
31
|
|
32
32
|
# Multi properties
|
33
33
|
ical_multiline_property :attendee, :attendee, :attendees
|
34
|
-
|
34
|
+
ical_multiline_property :attach, :attachment, :attachments
|
35
35
|
|
36
36
|
def initialize()
|
37
37
|
super("VALARM")
|
@@ -27,6 +27,9 @@ module Icalendar
|
|
27
27
|
# Complete description of the calendar component
|
28
28
|
ical_property :description
|
29
29
|
|
30
|
+
# Specifies the timezone for the event
|
31
|
+
attr_accessor :tzid
|
32
|
+
|
30
33
|
# Specifies date-time when calendar component begins
|
31
34
|
ical_property :dtstart, :start
|
32
35
|
|
@@ -117,7 +120,10 @@ module Icalendar
|
|
117
120
|
|
118
121
|
a
|
119
122
|
end
|
120
|
-
|
123
|
+
|
124
|
+
def occurrences_starting(time)
|
125
|
+
recurrence_rules.first.occurrences_of_event_starting(self, time)
|
126
|
+
end
|
121
127
|
|
122
128
|
end
|
123
129
|
end
|
@@ -50,19 +50,37 @@ module Icalendar
|
|
50
50
|
# of components.
|
51
51
|
def to_ical
|
52
52
|
print_component do
|
53
|
-
|
53
|
+
s = ""
|
54
54
|
@components.each_value do |comp|
|
55
55
|
s << comp.to_ical
|
56
56
|
end
|
57
|
-
|
57
|
+
s
|
58
58
|
end
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
|
62
62
|
def initialize(name = "VTIMEZONE")
|
63
63
|
super(name)
|
64
64
|
end
|
65
|
+
|
66
|
+
# Allow block syntax for declaration of standard and daylight components of timezone
|
67
|
+
def standard(&block)
|
68
|
+
e = Standard.new
|
69
|
+
self.add_component e
|
65
70
|
|
71
|
+
e.instance_eval &block if block
|
72
|
+
|
73
|
+
e
|
74
|
+
end
|
75
|
+
|
76
|
+
def daylight(&block)
|
77
|
+
e = Daylight.new
|
78
|
+
self.add_component e
|
79
|
+
|
80
|
+
e.instance_eval &block if block
|
81
|
+
|
82
|
+
e
|
83
|
+
end
|
66
84
|
end
|
67
85
|
|
68
86
|
# A Standard component is a sub-component of the Timezone component which
|
@@ -16,6 +16,12 @@ require 'date'
|
|
16
16
|
# end
|
17
17
|
# end
|
18
18
|
|
19
|
+
module Icalendar
|
20
|
+
module TzidSupport
|
21
|
+
attr_accessor :icalendar_tzid
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
19
25
|
require 'uri/generic'
|
20
26
|
|
21
27
|
class String
|
@@ -53,7 +59,10 @@ module URI
|
|
53
59
|
end
|
54
60
|
|
55
61
|
class DateTime < Date
|
56
|
-
|
62
|
+
attr_accessor :ical_params
|
63
|
+
include Icalendar::TzidSupport
|
64
|
+
|
65
|
+
def to_ical
|
57
66
|
s = ""
|
58
67
|
|
59
68
|
# 4 digit year
|
@@ -82,7 +91,7 @@ class DateTime < Date
|
|
82
91
|
s << self.sec.to_s
|
83
92
|
|
84
93
|
# UTC time gets a Z suffix
|
85
|
-
if
|
94
|
+
if icalendar_tzid == "UTC"
|
86
95
|
s << "Z"
|
87
96
|
end
|
88
97
|
|
@@ -91,6 +100,7 @@ class DateTime < Date
|
|
91
100
|
end
|
92
101
|
|
93
102
|
class Date
|
103
|
+
attr_accessor :ical_params
|
94
104
|
def to_ical(utc = false)
|
95
105
|
s = ""
|
96
106
|
|
@@ -108,6 +118,7 @@ class Date
|
|
108
118
|
end
|
109
119
|
|
110
120
|
class Time
|
121
|
+
attr_accessor :ical_params
|
111
122
|
def to_ical(utc = false)
|
112
123
|
s = ""
|
113
124
|
|
data/lib/icalendar/parser.rb
CHANGED
@@ -12,6 +12,101 @@ require 'uri'
|
|
12
12
|
require 'stringio'
|
13
13
|
|
14
14
|
module Icalendar
|
15
|
+
class RRule
|
16
|
+
|
17
|
+
class Weekday
|
18
|
+
def initialize(day, position)
|
19
|
+
@day, @position = day, position
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"#{@position}#{@day}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(name, params, value, parser)
|
28
|
+
frequency_match = value.match(/FREQ=(SECONDLY|MINUTELY|HOURLY|DAILY|WEEKLY|MONTHLY|YEARLY)/)
|
29
|
+
raise Icalendar::InvalidPropertyValue.new("FREQ must be specified for RRULE values") unless frequency_match
|
30
|
+
@frequency = frequency_match[1]
|
31
|
+
@until = parse_date_val("UNTIL", value)
|
32
|
+
@count = parse_int_val("COUNT", value)
|
33
|
+
raise Icalendar::InvalidPropertyValue.new("UNTIL and COUNT must not both be specified for RRULE values") if [@until, @count].compact.length > 1
|
34
|
+
@interval = parse_int_val("INTERVAL", value)
|
35
|
+
@by_list = {:bysecond => parse_int_list("BYSECOND", value)}
|
36
|
+
@by_list[:byminute] = parse_int_list("BYMINUTE",value)
|
37
|
+
@by_list[:byhour] = parse_int_list("BYHOUR", value)
|
38
|
+
@by_list[:byday] = parse_weekday_list("BYDAY", value)
|
39
|
+
@by_list[:bymonthday] = parse_int_list("BYMONTHDAY", value)
|
40
|
+
@by_list[:byyearday] = parse_int_list("BYYEARDAY", value)
|
41
|
+
@by_list[:byweekno] = parse_int_list("BYWEEKNO", value)
|
42
|
+
@by_list[:bymonth] = parse_int_list("BYMONTH", value)
|
43
|
+
@by_list[:bysetpos] = parse_int_list("BYSETPOS", value)
|
44
|
+
@wkst = parse_wkstart(value)
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_ical
|
48
|
+
result = ["FREQ=#{@frequency}"]
|
49
|
+
result << ";UNTIL=#{@until.to_ical}" if @until
|
50
|
+
result << ";COUNT=#{@count}" if @count
|
51
|
+
result << ";INTERVAL=#{@interval}" if @interval
|
52
|
+
@by_list.each do |key, value|
|
53
|
+
result << ";#{key.to_s.upcase}=#{value}" if value
|
54
|
+
end
|
55
|
+
result << ";WKST=#{@wkst}" if @wkst
|
56
|
+
result.join
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_date_val(name, string)
|
60
|
+
match = string.match(/;#{name}=(.*?)(;|$)/)
|
61
|
+
match ? DateTime.parse(match[1]) : nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse_int_val(name, string)
|
65
|
+
match = string.match(/;#{name}=(\d+)(;|$)/)
|
66
|
+
match ? match[1].to_i : nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse_int_list(name, string)
|
70
|
+
match = string.match(/;#{name}=([+-]?.*?)(;|$)/)
|
71
|
+
if match
|
72
|
+
match[1].split(",").map {|int| int.to_i}
|
73
|
+
else
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def parse_weekday_list(name, string)
|
79
|
+
match = string.match(/;#{name}=(.*?)(;|$)/)
|
80
|
+
if match
|
81
|
+
match[1].split(",").map {|weekday|
|
82
|
+
wd_match = weekday.match(/([+-]?\d*)(SU|MO|TU|WE|TH|FR|SA)/)
|
83
|
+
Weekday.new(wd_match[2], wd_match[1])
|
84
|
+
}
|
85
|
+
else
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def parse_wkstart(string)
|
91
|
+
match = string.match(/;WKSTART=(SU|MO|TU|WE|TH|FR|SA)(;|$)/)
|
92
|
+
if match
|
93
|
+
%w{SU MO TU WE TH FR SA}.index(match[1])
|
94
|
+
else
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# TODO: Incomplete
|
100
|
+
def occurrences_of_event_starting(event, datetime)
|
101
|
+
initial_start = event.dtstart
|
102
|
+
(0...@count).map {|day_offset|
|
103
|
+
occurrence = event.clone
|
104
|
+
occurrence.dtstart = initial_start + day_offset
|
105
|
+
occurrence.clone
|
106
|
+
}
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
15
110
|
def Icalendar.parse(src, single = false)
|
16
111
|
cals = Icalendar::Parser.new(src).parse
|
17
112
|
|
@@ -153,6 +248,7 @@ module Icalendar
|
|
153
248
|
|
154
249
|
# Lookup the property name to see if we have a string to
|
155
250
|
# object parser for this property type.
|
251
|
+
orig_value = value
|
156
252
|
if @parsers.has_key?(name)
|
157
253
|
value = @parsers[name].call(name, params, value)
|
158
254
|
end
|
@@ -223,7 +319,7 @@ module Icalendar
|
|
223
319
|
pname = $1
|
224
320
|
pvals = $3
|
225
321
|
|
226
|
-
# If
|
322
|
+
# If there isn't an '=' sign then we need to do some custom
|
227
323
|
# business. Defaults to 'type'
|
228
324
|
if $2 == ""
|
229
325
|
pvals = $1
|
@@ -298,6 +394,11 @@ module Icalendar
|
|
298
394
|
# GEO
|
299
395
|
m = self.method(:parse_geo)
|
300
396
|
@parsers["GEO"] = m
|
397
|
+
|
398
|
+
#RECUR
|
399
|
+
m = self.method(:parse_recur)
|
400
|
+
@parsers["RRULE"] = m
|
401
|
+
@parsers["EXRULE"] = m
|
301
402
|
|
302
403
|
end
|
303
404
|
|
@@ -317,11 +418,25 @@ module Icalendar
|
|
317
418
|
# NOTE: invalid dates & times will be returned as strings...
|
318
419
|
def parse_datetime(name, params, value)
|
319
420
|
begin
|
320
|
-
|
421
|
+
if params["VALUE"] && params["VALUE"].first == "DATE"
|
422
|
+
result = Date.parse(value)
|
423
|
+
else
|
424
|
+
result = DateTime.parse(value)
|
425
|
+
if /Z$/ =~ value
|
426
|
+
timezone = "UTC"
|
427
|
+
else
|
428
|
+
timezone = params["TZID"].first if params["TZID"]
|
429
|
+
end
|
430
|
+
result.icalendar_tzid = timezone
|
431
|
+
end
|
432
|
+
result
|
321
433
|
rescue Exception
|
322
434
|
value
|
323
435
|
end
|
324
|
-
|
436
|
+
end
|
437
|
+
|
438
|
+
def parse_recur(name, params, value)
|
439
|
+
::Icalendar::RRule.new(name, params, value, self)
|
325
440
|
end
|
326
441
|
|
327
442
|
# Durations
|
@@ -0,0 +1,121 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 Sean Dague
|
3
|
+
|
4
|
+
This library is free software; you can redistribute it and/or modify it
|
5
|
+
under the same terms as the ruby language itself, see the file COPYING for
|
6
|
+
details.
|
7
|
+
=end
|
8
|
+
|
9
|
+
# The following adds a bunch of mixins to the tzinfo class, with the
|
10
|
+
# intent on making it very easy to load in tzinfo data for generating
|
11
|
+
# ical events. With this you can do the following:
|
12
|
+
#
|
13
|
+
# require "icalendar/tzinfo"
|
14
|
+
#
|
15
|
+
# estart = DateTime.new(2008, 12, 29, 8, 0, 0)
|
16
|
+
# eend = DateTime.new(2008, 12, 29, 11, 0, 0)
|
17
|
+
# tstring = "America/Chicago"
|
18
|
+
#
|
19
|
+
# tz = TZInfo::Timezone.get(tstring)
|
20
|
+
# cal = Calendar.new
|
21
|
+
# # the mixins now generate all the timezone info for the date in question
|
22
|
+
# timezone = tz.ical_timezone(estart)
|
23
|
+
# cal.add(timezone)
|
24
|
+
#
|
25
|
+
# cal.event do
|
26
|
+
# dtstart estart
|
27
|
+
# dtend eend
|
28
|
+
# summary "Meeting with the man."
|
29
|
+
# description "Have a long lunch meeting and decide nothing..."
|
30
|
+
# klass "PRIVATE"
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# puts cal.to_ical
|
34
|
+
#
|
35
|
+
# The recurance rule calculations are hacky, and only start at the
|
36
|
+
# beginning of the current dst transition. I doubt this works for non
|
37
|
+
# dst areas yet. However, for a standard dst flipping zone, this
|
38
|
+
# seems to work fine (tested in Mozilla Thunderbird + Lightning).
|
39
|
+
# Future goal would be making this better.
|
40
|
+
|
41
|
+
# require "rubygems"
|
42
|
+
# require "tzinfo"
|
43
|
+
|
44
|
+
module TZInfo
|
45
|
+
class Timezone
|
46
|
+
def ical_timezone(date)
|
47
|
+
period = period_for_local(date)
|
48
|
+
timezone = Icalendar::Timezone.new
|
49
|
+
timezone.timezone_id = identifier
|
50
|
+
timezone.add(period.daylight)
|
51
|
+
timezone.add(period.standard)
|
52
|
+
return timezone
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class TimezoneTransitionInfo
|
57
|
+
def offset_from
|
58
|
+
a = previous_offset.utc_total_offset
|
59
|
+
sprintf("%2.2d%2.2d", (a / 3600).to_i, ((a / 60) % 60).to_i)
|
60
|
+
end
|
61
|
+
|
62
|
+
def offset_to
|
63
|
+
a = offset.utc_total_offset
|
64
|
+
sprintf("%2.2d%2.2d", (a / 3600).to_i, ((a / 60) % 60).to_i)
|
65
|
+
end
|
66
|
+
|
67
|
+
def rrule
|
68
|
+
start = local_start.to_datetime
|
69
|
+
# this is somewhat of a hack, but seems to work ok
|
70
|
+
[sprintf(
|
71
|
+
"FREQ=YEARLY;BYMONTH=%d;BYDAY=%d%s",
|
72
|
+
start.month,
|
73
|
+
((start.day - 1)/ 7).to_i + 1,
|
74
|
+
start.strftime("%a").upcase[0,2]
|
75
|
+
)]
|
76
|
+
end
|
77
|
+
|
78
|
+
def dtstart
|
79
|
+
local_start.to_datetime.strftime("%Y%m%dT%H%M%S")
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
class TimezonePeriod
|
85
|
+
def daylight
|
86
|
+
day = Icalendar::Daylight.new
|
87
|
+
if dst?
|
88
|
+
day.timezone_name = abbreviation.to_s
|
89
|
+
day.timezone_offset_from = start_transition.offset_from
|
90
|
+
day.timezone_offset_to = start_transition.offset_to
|
91
|
+
day.dtstart = start_transition.dtstart
|
92
|
+
day.recurrence_rules = start_transition.rrule
|
93
|
+
else
|
94
|
+
day.timezone_name = abbreviation.to_s.sub("ST","DT")
|
95
|
+
day.timezone_offset_from = end_transition.offset_from
|
96
|
+
day.timezone_offset_to = end_transition.offset_to
|
97
|
+
day.dtstart = end_transition.dtstart
|
98
|
+
day.recurrence_rules = end_transition.rrule
|
99
|
+
end
|
100
|
+
return day
|
101
|
+
end
|
102
|
+
|
103
|
+
def standard
|
104
|
+
std = Icalendar::Standard.new
|
105
|
+
if dst?
|
106
|
+
std.timezone_name = abbreviation.to_s.sub("DT","ST")
|
107
|
+
std.timezone_offset_from = end_transition.offset_from
|
108
|
+
std.timezone_offset_to = end_transition.offset_to
|
109
|
+
std.dtstart = end_transition.dtstart
|
110
|
+
std.recurrence_rules = end_transition.rrule
|
111
|
+
else
|
112
|
+
std.timezone_name = abbreviation.to_s
|
113
|
+
std.timezone_offset_from = start_transition.offset_from
|
114
|
+
std.timezone_offset_to = start_transition.offset_to
|
115
|
+
std.dtstart = start_transition.dtstart
|
116
|
+
std.recurrence_rules = start_transition.rrule
|
117
|
+
end
|
118
|
+
return std
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -3,6 +3,14 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
|
3
3
|
require 'test/unit'
|
4
4
|
require 'icalendar'
|
5
5
|
|
6
|
+
unless defined? 1.days
|
7
|
+
class Integer
|
8
|
+
def days
|
9
|
+
self * 60 * 60 * 24
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
6
14
|
# Define a test event
|
7
15
|
testEvent = <<EOS
|
8
16
|
BEGIN:VEVENT
|
@@ -40,5 +48,209 @@ class TestEvent < Test::Unit::TestCase
|
|
40
48
|
|
41
49
|
assert_equal("PRIVATE", event2.ip_class)
|
42
50
|
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class TestEventWithSpecifiedTimezone < Test::Unit::TestCase
|
54
|
+
|
55
|
+
def setup
|
56
|
+
src = <<EOS
|
57
|
+
BEGIN:VCALENDAR
|
58
|
+
METHOD:PUBLISH
|
59
|
+
CALSCALE:GREGORIAN
|
60
|
+
VERSION:2.0
|
61
|
+
BEGIN:VEVENT
|
62
|
+
UID:19970901T130000Z-123401@host.com
|
63
|
+
DTSTAMP:19970901T1300Z
|
64
|
+
DTSTART;TZID=America/Chicago:19970903T163000
|
65
|
+
DTEND;TZID=America/Chicago:19970903T190000
|
66
|
+
SUMMARY:Annual Employee Review
|
67
|
+
CLASS:PRIVATE
|
68
|
+
CATEGORIES:BUSINESS,HUMAN RESOURCES
|
69
|
+
END:VEVENT
|
70
|
+
END:VCALENDAR
|
71
|
+
EOS
|
72
|
+
@calendar = Icalendar.parse(src).first
|
73
|
+
@event = @calendar.events.first
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_event_is_parsed
|
77
|
+
assert_not_nil(@event)
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_dtstart_should_understand_icalendar_tzid
|
81
|
+
assert_respond_to(@event.dtstart, :icalendar_tzid)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_dtstart_tzid_should_be_correct
|
85
|
+
puts "#{@event.dtstart.icalendar_tzid} #{@event.dtstart}"
|
86
|
+
assert_equal("America/Chicago",@event.dtstart.icalendar_tzid)
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_dtend_tzid_should_be_correct
|
90
|
+
assert_equal("America/Chicago",@event.dtend.icalendar_tzid)
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
class TestEventWithZuluTimezone < Test::Unit::TestCase
|
96
|
+
|
97
|
+
def setup
|
98
|
+
src = <<EOS
|
99
|
+
BEGIN:VCALENDAR
|
100
|
+
METHOD:PUBLISH
|
101
|
+
CALSCALE:GREGORIAN
|
102
|
+
VERSION:2.0
|
103
|
+
BEGIN:VEVENT
|
104
|
+
UID:19970901T130000Z-123401@host.com
|
105
|
+
DTSTAMP:19970901T1300Z
|
106
|
+
DTSTART:19970903T163000Z
|
107
|
+
DTEND:19970903T190000Z
|
108
|
+
SUMMARY:Annual Employee Review
|
109
|
+
CLASS:PRIVATE
|
110
|
+
CATEGORIES:BUSINESS,HUMAN RESOURCES
|
111
|
+
END:VEVENT
|
112
|
+
END:VCALENDAR
|
113
|
+
EOS
|
114
|
+
@calendar = Icalendar.parse(src).first
|
115
|
+
@event = @calendar.events.first
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_event_is_parsed
|
119
|
+
assert_not_nil(@event)
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_dtstart_tzid_should_be_correct
|
123
|
+
puts "#{@event.dtstart.icalendar_tzid} #{@event.dtstart}"
|
124
|
+
assert_equal("UTC",@event.dtstart.icalendar_tzid)
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_dtend_tzid_should_be_correct
|
128
|
+
assert_equal("UTC",@event.dtend.icalendar_tzid)
|
129
|
+
end
|
43
130
|
|
44
131
|
end
|
132
|
+
|
133
|
+
class TestEventWithFloatingTimezone < Test::Unit::TestCase
|
134
|
+
|
135
|
+
def setup
|
136
|
+
src = <<EOS
|
137
|
+
BEGIN:VCALENDAR
|
138
|
+
METHOD:PUBLISH
|
139
|
+
CALSCALE:GREGORIAN
|
140
|
+
VERSION:2.0
|
141
|
+
BEGIN:VEVENT
|
142
|
+
UID:19970901T130000Z-123401@host.com
|
143
|
+
DTSTAMP:19970901T1300Z
|
144
|
+
DTSTART:19970903T163000
|
145
|
+
DTEND:19970903T190000
|
146
|
+
SUMMARY:Annual Employee Review
|
147
|
+
CLASS:PRIVATE
|
148
|
+
CATEGORIES:BUSINESS,HUMAN RESOURCES
|
149
|
+
END:VEVENT
|
150
|
+
END:VCALENDAR
|
151
|
+
EOS
|
152
|
+
@calendar = Icalendar.parse(src).first
|
153
|
+
@event = @calendar.events.first
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_event_is_parsed
|
157
|
+
assert_not_nil(@event)
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_dtstart_tzid_should_be_nil
|
161
|
+
puts "#{@event.dtstart.icalendar_tzid.inspect} #{@event.dtstart}"
|
162
|
+
assert_nil(@event.dtstart.icalendar_tzid)
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_dtend_tzid_should_be_nil
|
166
|
+
assert_nil(@event.dtend.icalendar_tzid)
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
class TestAllDayEventWithoutTime < Test::Unit::TestCase
|
172
|
+
|
173
|
+
def setup
|
174
|
+
src = <<EOS
|
175
|
+
BEGIN:VCALENDAR
|
176
|
+
VERSION:2.0
|
177
|
+
X-WR-CALNAME:New Event
|
178
|
+
PRODID:-//Apple Computer\, Inc//iCal 2.0//EN
|
179
|
+
X-WR-RELCALID:3A016BE7-8932-4456-8ABD-C8F7EEC5963A
|
180
|
+
X-WR-TIMEZONE:Europe/London
|
181
|
+
CALSCALE:GREGORIAN
|
182
|
+
METHOD:PUBLISH
|
183
|
+
BEGIN:VEVENT
|
184
|
+
DTSTART;VALUE=DATE:20090110
|
185
|
+
DTEND;VALUE=DATE:20090111
|
186
|
+
SUMMARY:New Event
|
187
|
+
UID:3829F33C-F601-49AC-A3A5-C3AC4A6A3483
|
188
|
+
SEQUENCE:4
|
189
|
+
DTSTAMP:20090109T184719Z
|
190
|
+
END:VEVENT
|
191
|
+
END:VCALENDAR
|
192
|
+
EOS
|
193
|
+
@calendar = Icalendar.parse(src).first
|
194
|
+
@event = @calendar.events.first
|
195
|
+
end
|
196
|
+
|
197
|
+
def test_event_is_parsed
|
198
|
+
assert_not_nil(@event)
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_dtstart_set_correctly
|
202
|
+
assert_equal("20090110", @event.dtstart.to_ical)
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
class TestRecurringEventWithCount < Test::Unit::TestCase
|
208
|
+
# DTSTART;TZID=US-Eastern:19970902T090000
|
209
|
+
# RRULE:FREQ=DAILY;COUNT=10
|
210
|
+
# ==> (1997 9:00 AM EDT)September 2-11
|
211
|
+
|
212
|
+
def setup
|
213
|
+
src = <<EOS
|
214
|
+
BEGIN:VCALENDAR
|
215
|
+
METHOD:PUBLISH
|
216
|
+
CALSCALE:GREGORIAN
|
217
|
+
VERSION:2.0
|
218
|
+
BEGIN:VEVENT
|
219
|
+
UID:19970901T130000Z-123401@host.com
|
220
|
+
DTSTAMP:19970901T1300Z
|
221
|
+
DTSTART:19970902T090000Z
|
222
|
+
DTEND:19970902T100000Z
|
223
|
+
RRULE:FREQ=DAILY;COUNT=10
|
224
|
+
SUMMARY:Annual Employee Review
|
225
|
+
CLASS:PRIVATE
|
226
|
+
CATEGORIES:BUSINESS,HUMAN RESOURCES
|
227
|
+
END:VEVENT
|
228
|
+
END:VCALENDAR
|
229
|
+
EOS
|
230
|
+
@calendar = Icalendar.parse(src).first
|
231
|
+
@event = @calendar.events.first
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_event_is_parsed
|
235
|
+
assert_not_nil(@event)
|
236
|
+
end
|
237
|
+
|
238
|
+
def test_recurrence_rules_should_return_a_recurrence_rule_array
|
239
|
+
assert_equal 1, @event.recurrence_rules.length
|
240
|
+
assert_kind_of(Icalendar::RRule, @event.recurrence_rules.first)
|
241
|
+
end
|
242
|
+
|
243
|
+
def test_occurrences_after_with_start_before_start_at_should_return_count_occurrences
|
244
|
+
assert_equal 10, @event.occurrences_starting(Time.utc(1997, 9, 2, 8, 30, 0, 0)).length
|
245
|
+
end
|
246
|
+
|
247
|
+
# def test_occurrences_after_with_start_before_start_at_should_return_an_event_with_the_dtstart_as_the_first_event
|
248
|
+
# assert_equal @event.dtstart.to_s, @event.occurrences_starting(Time.utc(1997, 9, 2, 8, 30, 0, 0)).first.dtstart.to_s
|
249
|
+
# end
|
250
|
+
#
|
251
|
+
# def test_occurrences_after_with_start_before_start_at_should_return_events_with_the_correct_dtstart_values
|
252
|
+
# expected = (0..9).map {|delta| (@event.dtstart + delta).to_s}
|
253
|
+
# assert_equal expected, @event.occurrences_starting(Time.utc(1997, 9, 2, 8, 30, 0, 0)).map {|occurence| occurence.dtstart.to_s}
|
254
|
+
# end
|
255
|
+
end
|
256
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'icalendar'
|
5
|
+
|
6
|
+
class TestTimezone < Test::Unit::TestCase
|
7
|
+
|
8
|
+
# Create a calendar with an event for each test.
|
9
|
+
def setup
|
10
|
+
@cal = Icalendar::Calendar.new
|
11
|
+
# Define a test timezone
|
12
|
+
@testTimezone = %Q(BEGIN:VTIMEZONE\r\nTZID:America/Chicago\r\nBEGIN:STANDARD\r\nTZOFFSETTO:-0600\r\nRRULE:FREQ=YEARLY\\;BYMONTH=11\\;BYDAY=1SU\r\nTZOFFSETFROM:-0500\r\nDTSTART:19701101T020000\r\nTZNAME:CST\r\nEND:STANDARD\r\nBEGIN:DAYLIGHT\r\nTZOFFSETTO:-0500\r\nRRULE:FREQ=YEARLY\\;BYMONTH=3\\;BYDAY=2SU\r\nTZOFFSETFROM:-0600\r\nDTSTART:19700308TO20000\r\nTZNAME:CDT\r\nEND:DAYLIGHT\r\nEND:VTIMEZONE\r\n)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_new
|
16
|
+
@tz = Icalendar::Timezone.new
|
17
|
+
assert(@tz)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_raw_generation
|
21
|
+
timezone = Icalendar::Timezone.new
|
22
|
+
daylight = Icalendar::Daylight.new
|
23
|
+
standard = Icalendar::Standard.new
|
24
|
+
|
25
|
+
timezone.timezone_id = "America/Chicago"
|
26
|
+
|
27
|
+
daylight.timezone_offset_from = "-0600"
|
28
|
+
daylight.timezone_offset_to = "-0500"
|
29
|
+
daylight.timezone_name = "CDT"
|
30
|
+
daylight.dtstart = "19700308TO20000"
|
31
|
+
daylight.recurrence_rules = ["FREQ=YEARLY;BYMONTH=3;BYDAY=2SU"]
|
32
|
+
|
33
|
+
standard.timezone_offset_from = "-0500"
|
34
|
+
standard.timezone_offset_to = "-0600"
|
35
|
+
standard.timezone_name = "CST"
|
36
|
+
standard.dtstart = "19701101T020000"
|
37
|
+
standard.recurrence_rules = ["FREQ=YEARLY;BYMONTH=11;BYDAY=1SU"]
|
38
|
+
|
39
|
+
timezone.add(standard)
|
40
|
+
timezone.add(daylight)
|
41
|
+
@cal.add(timezone)
|
42
|
+
assert_equal(@testTimezone, @cal.timezones.first.to_ical)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_block_creation
|
46
|
+
@cal.timezone do
|
47
|
+
timezone_id "America/Chicago"
|
48
|
+
|
49
|
+
daylight do
|
50
|
+
timezone_offset_from "-0600"
|
51
|
+
timezone_offset_to "-0500"
|
52
|
+
timezone_name "CDT"
|
53
|
+
dtstart "19700308TO20000"
|
54
|
+
add_recurrence_rule "FREQ=YEARLY;BYMONTH=3;BYDAY=2SU"
|
55
|
+
end
|
56
|
+
|
57
|
+
standard do
|
58
|
+
timezone_offset_from "-0500"
|
59
|
+
timezone_offset_to "-0600"
|
60
|
+
timezone_name "CST"
|
61
|
+
dtstart "19701101T020000"
|
62
|
+
add_recurrence_rule "FREQ=YEARLY;BYMONTH=11;BYDAY=1SU"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
assert_equal(@testTimezone, @cal.timezones.first.to_ical)
|
66
|
+
end
|
67
|
+
end
|
data/test/conversions_test.rb
CHANGED
@@ -18,7 +18,7 @@ LAST-MODIFIED:19960817T133000
|
|
18
18
|
SEQUENCE:2
|
19
19
|
ORGANIZER:mailto:joe@example.com?subject=Ruby
|
20
20
|
UID:foobar
|
21
|
-
X-TIME-OF-DAY:
|
21
|
+
X-TIME-OF-DAY:101736
|
22
22
|
CATEGORIES:foo\\,bar\\,baz
|
23
23
|
GEO:46.01\\;8.57
|
24
24
|
DESCRIPTION:desc
|
@@ -60,7 +60,7 @@ EOS
|
|
60
60
|
timestamp DateTime.parse("2006-07-20T17:40:52+0200")
|
61
61
|
|
62
62
|
# Time
|
63
|
-
x_time_of_day Time.at(123456)
|
63
|
+
x_time_of_day Time.at(123456).utc
|
64
64
|
|
65
65
|
uid "foobar"
|
66
66
|
end
|
data/test/coverage/STUB
ADDED
File without changes
|
metadata
CHANGED
@@ -1,98 +1,109 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.4
|
3
|
-
specification_version: 1
|
4
2
|
name: icalendar
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 1.0
|
7
|
-
date: 2007-11-25 00:00:00 +01:00
|
8
|
-
summary: A ruby implementation of the iCalendar specification (RFC-2445).
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email: rosejn@gmail.com
|
12
|
-
homepage: http://icalendar.rubyforge.org/
|
13
|
-
rubyforge_project:
|
14
|
-
description: Implements the iCalendar specification (RFC-2445) in Ruby. This allows for the generation and parsing of .ics files, which are used by a variety of calendaring applications.
|
15
|
-
autorequire: icalendar
|
16
|
-
default_executable:
|
17
|
-
bindir: bin
|
18
|
-
has_rdoc: true
|
19
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">"
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 0.0.0
|
24
|
-
version:
|
4
|
+
version: 1.1.0
|
25
5
|
platform: ruby
|
26
|
-
signing_key:
|
27
|
-
cert_chain:
|
28
|
-
post_install_message:
|
29
6
|
authors:
|
30
|
-
-
|
7
|
+
- Sean Dague
|
8
|
+
autorequire: icalendar
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-01-29 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Implements the iCalendar specification (RFC-2445) in Ruby. This allows for the generation and parsing of .ics files, which are used by a variety of calendaring applications.
|
17
|
+
email: sean@dague.net
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
- COPYING
|
25
|
+
- GPL
|
31
26
|
files:
|
32
|
-
- test/
|
33
|
-
- test/component/event_test.rb
|
34
|
-
- test/component/todo_test.rb
|
27
|
+
- test/calendar_test.rb
|
35
28
|
- test/parameter_test.rb
|
36
29
|
- test/interactive.rb
|
37
|
-
- test/
|
30
|
+
- test/conversions_test.rb
|
38
31
|
- test/component_test.rb
|
32
|
+
- test/parser_test.rb
|
33
|
+
- test/read_write.rb
|
39
34
|
- test/fixtures
|
40
|
-
- test/fixtures/simplecal.ics
|
41
|
-
- test/fixtures/folding.ics
|
42
35
|
- test/fixtures/single_event.ics
|
36
|
+
- test/fixtures/folding.ics
|
37
|
+
- test/fixtures/simplecal.ics
|
43
38
|
- test/fixtures/life.ics
|
44
|
-
- test/
|
45
|
-
- test/
|
46
|
-
- test/
|
47
|
-
- test/
|
39
|
+
- test/component
|
40
|
+
- test/component/timezone_test.rb
|
41
|
+
- test/component/todo_test.rb
|
42
|
+
- test/component/event_test.rb
|
43
|
+
- test/coverage
|
44
|
+
- test/coverage/STUB
|
48
45
|
- lib/icalendar
|
46
|
+
- lib/icalendar/parameter.rb
|
47
|
+
- lib/icalendar/component.rb
|
48
|
+
- lib/icalendar/base.rb
|
49
|
+
- lib/icalendar/parser.rb
|
50
|
+
- lib/icalendar/tzinfo.rb
|
51
|
+
- lib/icalendar/calendar.rb
|
49
52
|
- lib/icalendar/component
|
50
53
|
- lib/icalendar/component/alarm.rb
|
54
|
+
- lib/icalendar/component/todo.rb
|
51
55
|
- lib/icalendar/component/event.rb
|
52
|
-
- lib/icalendar/component/freebusy.rb
|
53
56
|
- lib/icalendar/component/journal.rb
|
54
57
|
- lib/icalendar/component/timezone.rb
|
55
|
-
- lib/icalendar/component/
|
58
|
+
- lib/icalendar/component/freebusy.rb
|
56
59
|
- lib/icalendar/conversions.rb
|
57
|
-
- lib/icalendar/parameter.rb
|
58
|
-
- lib/icalendar/component.rb
|
59
60
|
- lib/icalendar/helpers.rb
|
60
|
-
- lib/icalendar/parser.rb
|
61
|
-
- lib/icalendar/calendar.rb
|
62
|
-
- lib/icalendar/base.rb
|
63
|
-
- lib/hash_attrs.rb
|
64
|
-
- lib/icalendar.rb
|
65
61
|
- lib/meta.rb
|
66
|
-
-
|
62
|
+
- lib/icalendar.rb
|
63
|
+
- lib/hash_attrs.rb
|
67
64
|
- docs/rfcs
|
65
|
+
- docs/rfcs/rfc2446.pdf
|
66
|
+
- docs/rfcs/rfc2426.pdf
|
68
67
|
- docs/rfcs/itip_notes.txt
|
68
|
+
- docs/rfcs/rfc2447.pdf
|
69
69
|
- docs/rfcs/rfc2425.pdf
|
70
|
-
- docs/rfcs/rfc2426.pdf
|
71
70
|
- docs/rfcs/rfc2445.pdf
|
72
|
-
- docs/rfcs/rfc2446.pdf
|
73
|
-
- docs/rfcs/rfc2447.pdf
|
74
71
|
- docs/rfcs/rfc3283.txt
|
72
|
+
- docs/api
|
73
|
+
- docs/api/STUB
|
75
74
|
- examples/single_event.ics
|
76
|
-
- examples/create_cal.rb
|
77
75
|
- examples/parse_cal.rb
|
76
|
+
- examples/create_cal.rb
|
78
77
|
- Rakefile
|
79
78
|
- README
|
80
79
|
- COPYING
|
81
80
|
- GPL
|
82
|
-
|
83
|
-
|
81
|
+
has_rdoc: true
|
82
|
+
homepage: http://icalendar.rubyforge.org/
|
83
|
+
post_install_message:
|
84
84
|
rdoc_options:
|
85
85
|
- --main
|
86
86
|
- README
|
87
|
-
|
88
|
-
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: "0"
|
94
|
+
version:
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: "0"
|
100
|
+
version:
|
95
101
|
requirements: []
|
96
102
|
|
97
|
-
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 1.2.0
|
105
|
+
signing_key:
|
106
|
+
specification_version: 2
|
107
|
+
summary: A ruby implementation of the iCalendar specification (RFC-2445).
|
108
|
+
test_files: []
|
98
109
|
|