vpim2 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +504 -0
  3. data/COPYING +58 -0
  4. data/README +182 -0
  5. data/lib/atom.rb +728 -0
  6. data/lib/plist.rb +22 -0
  7. data/lib/vpim.rb +13 -0
  8. data/lib/vpim/address.rb +219 -0
  9. data/lib/vpim/attachment.rb +102 -0
  10. data/lib/vpim/date.rb +222 -0
  11. data/lib/vpim/dirinfo.rb +277 -0
  12. data/lib/vpim/duration.rb +119 -0
  13. data/lib/vpim/enumerator.rb +32 -0
  14. data/lib/vpim/field.rb +614 -0
  15. data/lib/vpim/icalendar.rb +381 -0
  16. data/lib/vpim/maker/vcard.rb +16 -0
  17. data/lib/vpim/property/base.rb +193 -0
  18. data/lib/vpim/property/common.rb +315 -0
  19. data/lib/vpim/property/location.rb +38 -0
  20. data/lib/vpim/property/priority.rb +43 -0
  21. data/lib/vpim/property/recurrence.rb +69 -0
  22. data/lib/vpim/property/resources.rb +24 -0
  23. data/lib/vpim/repo.rb +181 -0
  24. data/lib/vpim/rfc2425.rb +367 -0
  25. data/lib/vpim/rrule.rb +591 -0
  26. data/lib/vpim/vcard.rb +1430 -0
  27. data/lib/vpim/version.rb +18 -0
  28. data/lib/vpim/vevent.rb +187 -0
  29. data/lib/vpim/view.rb +90 -0
  30. data/lib/vpim/vjournal.rb +58 -0
  31. data/lib/vpim/vpim.rb +65 -0
  32. data/lib/vpim/vtodo.rb +103 -0
  33. data/samples/README.mutt +93 -0
  34. data/samples/ab-query.rb +57 -0
  35. data/samples/cmd-itip.rb +156 -0
  36. data/samples/ex_cpvcard.rb +55 -0
  37. data/samples/ex_get_vcard_photo.rb +22 -0
  38. data/samples/ex_mkv21vcard.rb +34 -0
  39. data/samples/ex_mkvcard.rb +64 -0
  40. data/samples/ex_mkyourown.rb +29 -0
  41. data/samples/ics-dump.rb +210 -0
  42. data/samples/ics-to-rss.rb +84 -0
  43. data/samples/mutt-aliases-to-vcf.rb +45 -0
  44. data/samples/osx-wrappers.rb +86 -0
  45. data/samples/reminder.rb +203 -0
  46. data/samples/rrule.rb +71 -0
  47. data/samples/tabbed-file-to-vcf.rb +390 -0
  48. data/samples/vcf-dump.rb +86 -0
  49. data/samples/vcf-lines.rb +61 -0
  50. data/samples/vcf-to-ics.rb +22 -0
  51. data/samples/vcf-to-mutt.rb +121 -0
  52. data/test/test_all.rb +17 -0
  53. data/test/test_date.rb +120 -0
  54. data/test/test_dur.rb +41 -0
  55. data/test/test_field.rb +156 -0
  56. data/test/test_ical.rb +415 -0
  57. data/test/test_repo.rb +158 -0
  58. data/test/test_rrule.rb +1030 -0
  59. data/test/test_vcard.rb +973 -0
  60. data/test/test_view.rb +79 -0
  61. metadata +117 -0
data/test/test_repo.rb ADDED
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'vpim/repo'
4
+ require 'vpim/agent/calendars'
5
+ require 'test/unit'
6
+
7
+ require 'pp'
8
+
9
+ module Enumerable
10
+ def count
11
+ self.inject(0){|i,_| i + 1}
12
+ end
13
+ end
14
+
15
+ class TestRepo < Test::Unit::TestCase
16
+ Apple3 = Vpim::Repo::Apple3
17
+ Directory = Vpim::Repo::Directory
18
+ Agent = Vpim::Agent
19
+ Path = Agent::Path
20
+ NotFound = Agent::NotFound
21
+
22
+ def setup
23
+ @testdir = Dir.getwd + "/test" #File.dirname($0) doesn't work with rcov :-(
24
+ @caldir = @testdir + "/calendars"
25
+ @eventsz = Dir[@caldir + "/**/*.ics"].size
26
+ assert(@testdir)
27
+ assert(test(?d, @caldir), "no caldir "+@caldir)
28
+ end
29
+
30
+ def _test_each(repo, eventsz)
31
+ repo.each do |cal|
32
+ assert_equal(eventsz, cal.events.count, cal.name)
33
+ assert("", File.extname(cal.name))
34
+ assert_equal(cal.displayed, true)
35
+ cal.events do |c|
36
+ assert_instance_of(Vpim::Icalendar::Vevent, c)
37
+ end
38
+ cal.events.each do |c|
39
+ assert_instance_of(Vpim::Icalendar::Vevent, c)
40
+ end
41
+ assert_equal(0, cal.todos.count)
42
+ assert(cal.encode)
43
+ end
44
+ end
45
+
46
+ def test_apple3
47
+ repo = Apple3.new(@caldir)
48
+
49
+ assert_equal(1, repo.count)
50
+
51
+ _test_each(repo, @eventsz)
52
+ end
53
+
54
+ def test_dir
55
+ assert(test(?d, @caldir))
56
+
57
+ repo = Directory.new(@caldir)
58
+
59
+ assert_equal(@eventsz, repo.count)
60
+
61
+ _test_each(repo, 1)
62
+ end
63
+
64
+ def assert_is_text_calendar(text)
65
+ lines = text.split("\n")
66
+ lines = lines.first, lines.last
67
+ assert_equal("BEGIN:VCALENDAR", lines.first.upcase, lines)
68
+ assert_equal("END:VCALENDAR", lines.last.upcase, lines)
69
+ end
70
+
71
+ def test_agent_calendars
72
+ repo = Apple3.new(@caldir)
73
+ rest = Agent::Calendars.new(repo)
74
+
75
+ out1, form = rest.get(Path.new("http://host/here", "/here"))
76
+ assert_equal("text/html", form)
77
+ #puts(out1)
78
+
79
+ out1, form = rest.get(Path.new("http://host/here/weather%2fLeavenworth", "/here"))
80
+ assert_equal("text/html", form)
81
+ #puts(out1)
82
+
83
+ out2, form = rest.get(Path.new("http://host/here/weather%2fLeavenworth/calendar", "/here"))
84
+ assert_equal("text/calendar", form)
85
+ assert_is_text_calendar(out2)
86
+
87
+ #assert_equal(out1, out2)
88
+
89
+ assert_raise(Vpim::Agent::NotFound) do
90
+ rest.get(Path.new("http://host/here/weather%2fLeavenworth/an_unknown_protocol", "/here"))
91
+ end
92
+ assert_raise(Vpim::Agent::NotFound) do
93
+ rest.get(Path.new("http://host/here/no_such_calendar", "/here"))
94
+ end
95
+
96
+ assert_equal(["","/","/"], Vpim::Agent::Path.split_path("/%2F/%2F"))
97
+ assert_equal(["/","/"], Vpim::Agent::Path.split_path("%2F/%2F"))
98
+ assert_equal(["calendars", "weather/Leavenworth"],
99
+ Vpim::Agent::Path.split_path("calendars/weather%2FLeavenworth"))
100
+ end
101
+
102
+ def test_agent_calendar_atom
103
+ repo = Apple3.new(@caldir)
104
+ rest = Agent::Calendars.new(repo)
105
+
106
+ out, form = rest.get(Path.new("http://host/here/weather%2fLeavenworth/atom", "/here"))
107
+ assert_equal("application/atom+xml", form)
108
+ #pp out
109
+ #assert_is_atom(out)
110
+ end
111
+
112
+ def _test_path_shift(url, shifts)
113
+ # last shift should be a nil
114
+ shifts << nil
115
+
116
+ # presence or absence of a trailing / should not affect shifting
117
+ ["", "/"].each do |trailer|
118
+ path = Path.new(url + trailer)
119
+ shifts.each do |_|
120
+ assert_equal(_, path.shift)
121
+ end
122
+ end
123
+ end
124
+
125
+ def test_path_shift
126
+ _test_path_shift("http://host.ex", [])
127
+ _test_path_shift("http://host.ex/a", ["a"])
128
+ _test_path_shift("http://host.ex/a/b", ["a", "b"])
129
+ _test_path_shift("http://host.ex/a/b/c", ["a", "b", "c"])
130
+ end
131
+
132
+ def _test_path_prefix(base, parts, shifts, prefix)
133
+ path = Path.new(base+parts.join("/"))
134
+ shifts.times{ path.shift }
135
+ assert_equal(prefix, path.prefix)
136
+ end
137
+
138
+ def test_path_prefix
139
+ _test_path_prefix("http://host.ex/", [], 0, "/")
140
+ _test_path_prefix("http://host.ex/", ["a"], 0, "/")
141
+ _test_path_prefix("http://host.ex/", ["a"], 1, "/")
142
+ _test_path_prefix("http://host.ex/", ["a"], 2, "/a/")
143
+ _test_path_prefix("http://host.ex/", ["a"], 3, "/a/")
144
+ _test_path_prefix("http://host.ex/", ["a", "b"], 0, "/")
145
+ _test_path_prefix("http://host.ex/", ["a", "b"], 1, "/")
146
+ _test_path_prefix("http://host.ex/", ["a", "b"], 2, "/a/")
147
+ _test_path_prefix("http://host.ex/", ["a", "b"], 3, "/a/b/")
148
+ end
149
+
150
+ def test_atomize
151
+ repo = Apple3.new(@caldir)
152
+ cal = repo.find{true}
153
+ a = Vpim::Agent::Atomize.new(cal)
154
+ assert( a.get(Path.new("http://example.com/path")))
155
+ end
156
+
157
+ end
158
+
@@ -0,0 +1,1030 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ENV['TZ'] = 'EST5EDT'
4
+
5
+ require 'vpim/rrule'
6
+ require 'vpim/icalendar'
7
+ require 'test/unit'
8
+
9
+ require 'pp'
10
+
11
+ =begin
12
+ class Date
13
+ alias :inspect :to_s
14
+ end
15
+ =end
16
+
17
+ class TestRrule < Test::Unit::TestCase
18
+ Rrule = Vpim::Rrule
19
+
20
+ #=begin
21
+ # Comment out these if you want printing!
22
+ def puts(*args)
23
+ end
24
+
25
+ def pp(*args)
26
+ end
27
+ #=end
28
+ def parse_vec(vec)
29
+ pp vec
30
+ vec = vec.gsub(/.*#.*\(/, '(')
31
+ pp vec
32
+ vec = vec.split("\n")
33
+ pp vec
34
+ ovec = []
35
+
36
+ vec.each do |v|
37
+ time = v[0,18]
38
+ dates = v[18,v.length - 18]
39
+
40
+ pp time
41
+ pp dates
42
+
43
+ time.gsub!(/\((\d\d\d\d) (\d):/) { "(#{$1} 0#{$2}:" }
44
+ dates.split(';').each do |mondays|
45
+ mon, days = mondays.split(' ')
46
+
47
+ # debug mon
48
+ # debug days
49
+ days.split(',').each do |d|
50
+ # debug d
51
+ r = d.split '-'
52
+ # debug r
53
+ case r.length
54
+ when 1 then r = [ r[0].to_i ]
55
+ when 2 then r = r[0].to_i .. r[1].to_i
56
+ else
57
+ raise "don't grok #{d}"
58
+ end
59
+ r.each do |d0|
60
+ # debug time, mon, d, d0
61
+ ovec << sprintf("%s%s %.2d", time, mon, d0)
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ ovec
68
+ end
69
+
70
+ def Test(rule, dtstart = nil, expected = nil)
71
+ puts "---> #{rule}"
72
+ puts " #{dtstart}" if dtstart
73
+
74
+ expected = parse_vec(expected)
75
+
76
+ pp expected.length
77
+
78
+ start = Time.now
79
+
80
+ if dtstart
81
+ start = Vpim::Rrule.time_from_rfc2425(dtstart)
82
+ end
83
+
84
+ rrule = Vpim::Rrule.new(start, rule)
85
+
86
+ # debug rrule
87
+
88
+ # count = 1
89
+ # rrule.each do |t|
90
+ # puts format("count=%3d %s", count, t.to_s)
91
+ # count += 1
92
+ # end
93
+
94
+ got = rrule.map { |t|
95
+ t.strftime("(%Y %I:%M %p %Z)%B %d")
96
+ }
97
+
98
+ if expected && got != expected
99
+ puts "length: got=#{got.length} expected=#{expected.length}"
100
+ (0..expected.length).each do |i|
101
+ if(got[i] != expected[i])
102
+ puts sprintf("%d: %34s %s %s", i, got[i], got[i] == expected[i] ? '==' : '!=', expected[i])
103
+ end
104
+ end
105
+ #p got
106
+ #p expected
107
+ end
108
+ assert_equal(expected, got)
109
+ end
110
+
111
+ def test_rfc2445_examples_daily
112
+
113
+ # Daily for 10 occurrences:
114
+ #
115
+ # DTSTART;TZID=US-Eastern:19970902T090000
116
+ # RRULE:FREQ=DAILY;COUNT=10
117
+ #
118
+ # ==> (1997 9:00 AM EDT)September 2-11
119
+
120
+ Test(
121
+ 'FREQ=DAILY;COUNT=10',
122
+ '19970902T090000',
123
+ <<VEC
124
+ # ==> (1997 9:00 AM EDT)September 2-11
125
+ VEC
126
+ )
127
+
128
+ # Daily until December 24, 1997:
129
+ #
130
+ # DTSTART;TZID=US-Eastern:19970902T090000
131
+ # RRULE:FREQ=DAILY;UNTIL=19971224T000000Z
132
+ #
133
+ # ==> (1997 9:00 AM EDT)September 2-30;October 1-25
134
+ # (1997 9:00 AM EST)October 26-31;November 1-30;December 1-23
135
+
136
+ Test(
137
+ 'FREQ=DAILY;UNTIL=19971224T000000Z',
138
+ '19970902T090000',
139
+ <<VEC
140
+ # ==> (1997 9:00 AM EDT)September 2-30;October 1-25
141
+ # (1997 9:00 AM EST)October 26-31;November 1-30;December 1-23
142
+ VEC
143
+ );
144
+
145
+ # Every other day - forever:
146
+ #
147
+ # DTSTART;TZID=US-Eastern:19970902T090000
148
+ # RRULE:FREQ=DAILY;INTERVAL=2
149
+ # ==> (1997 9:00 AM EDT)September2,4,6,8...24,26,28,30;
150
+ # October 2,4,6...20,22,24
151
+ # (1997 9:00 AM EST)October 26,28,30;November 1,3,5,7...25,27,29;
152
+ # Dec 1,3,...
153
+
154
+ Test(
155
+ 'FREQ=DAILY;INTERVAL=2;count=27',
156
+ '19970902T090000',
157
+ <<VEC
158
+ # ==> (1997 9:00 AM EDT)September 2,4,6,8,10,12,14,16,18,20,22,24,26,28,30;October 2,4,6,8,10,12,14,16,18,20,22,24
159
+ VEC
160
+ )
161
+
162
+ # Every 10 days, 5 occurrences:
163
+ #
164
+ # DTSTART;TZID=US-Eastern:19970902T090000
165
+ # RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5
166
+ #
167
+ # ==> (1997 9:00 AM EDT)September 2,12,22;October 2,12
168
+
169
+ Test(
170
+ 'FREQ=DAILY;COUNT=5;Interval=10',
171
+ '19970902T090000',
172
+ <<VEC
173
+ # ==> (1997 9:00 AM EDT)September 2,12,22;October 2,12
174
+ VEC
175
+ )
176
+
177
+ end
178
+
179
+ def test_rfc2445_examples_yearly
180
+ #
181
+ # Everyday in January, for 3 years:
182
+ #
183
+ # DTSTART;TZID=US-Eastern:19980101T090000
184
+ # RRULE:FREQ=YEARLY;UNTIL=20000131T090000Z;
185
+ # BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA
186
+ # or
187
+ # RRULE:FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1
188
+ #
189
+ # ==> (1998 9:00 AM EDT)January 1-31
190
+ # (1999 9:00 AM EDT)January 1-31
191
+ # (2000 9:00 AM EDT)January 1-31
192
+
193
+ # FIXME -
194
+ # I believe the UNTIL time, being in UTC, is BEFORE (2000 9:00 AM
195
+ # EDT)January 31 , so the last date in the result vector is not valid.
196
+ #
197
+ # Also, January is in EST, not EDT!
198
+
199
+ Test(
200
+ 'FREQ=YEARLY;UNTIL=20000131T090000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA',
201
+ '19980101T090000',
202
+ <<VEC
203
+ # ==> (1998 9:00 AM EST)January 1-31
204
+ # (1999 9:00 AM EST)January 1-31
205
+ # (2000 9:00 AM EST)January 1-30
206
+ VEC
207
+ )
208
+
209
+ Test(
210
+ 'FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1',
211
+ '19980101T090000',
212
+ <<VEC
213
+ # ==> (1998 9:00 AM EST)January 1-31
214
+ # (1999 9:00 AM EST)January 1-31
215
+ # (2000 9:00 AM EST)January 1-30
216
+ VEC
217
+ )
218
+ end
219
+
220
+ def test_rfc2445_examples_weekly_for_10_occurrences
221
+ # Weekly for 10 occurrences
222
+ #
223
+ # DTSTART;TZID=US-Eastern:19970902T090000
224
+ # RRULE:FREQ=WEEKLY;COUNT=10
225
+ #
226
+ # ==> (1997 9:00 AM EDT)September 2,9,16,23,30;October 7,14,21
227
+ # (1997 9:00 AM EST)October 28;November 4
228
+
229
+ Test(
230
+ 'FREQ=WEEKLY;COUNT=10',
231
+ '19970902T090000',
232
+ <<VEC
233
+ # ==> (1997 9:00 AM EDT)September 2,9,16,23,30;October 7,14,21
234
+ # (1997 9:00 AM EST)October 28;November 4
235
+ VEC
236
+ )
237
+ end
238
+
239
+ def test_rfc2445_examples_weekly_until_dec24_1997
240
+ # Weekly until December 24, 1997
241
+ #
242
+ # DTSTART;TZID=US-Eastern:19970902T090000
243
+ # RRULE:FREQ=WEEKLY;UNTIL=19971224T000000Z
244
+ #
245
+ # ==> (1997 9:00 AM EDT)September 2,9,16,23,30;October 7,14,21
246
+ # (1997 9:00 AM EST)October 28;November 4,11,18,25;December 2,9,16,23
247
+
248
+ Test(
249
+ 'FREQ=WEEKLY;UNTIL=19971224T000000Z',
250
+ '19970902T090000',
251
+ <<VEC
252
+ # ==> (1997 9:00 AM EDT)September 2,9,16,23,30;October 7,14,21
253
+ # (1997 9:00 AM EST)October 28;November 4,11,18,25;December 2,9,16,23
254
+ VEC
255
+ )
256
+
257
+ end
258
+
259
+ def test_rfc2445_examples_every_other_week_forever
260
+ # Every other week - forever:
261
+ #
262
+ # DTSTART;TZID=US-Eastern:19970902T090000
263
+ # RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU
264
+ #
265
+ # ==> (1997 9:00 AM EDT)September 2,16,30;October 14
266
+ # (1997 9:00 AM EST)October 28;November 11,25;December 9,23
267
+ # (1998 9:00 AM EST)January 6,20;February
268
+ # ...
269
+
270
+ Test(
271
+ 'FREQ=WEEKLY;INTERVAL=2;WKST=SU;count=11',
272
+ '19970902T090000',
273
+ <<VEC
274
+ # ==> (1997 9:00 AM EDT)September 2,16,30;October 14
275
+ # (1997 9:00 AM EST)October 28;November 11,25;December 9,23
276
+ # (1998 9:00 AM EST)January 6,20
277
+ VEC
278
+ )
279
+ end
280
+
281
+ def test_rfc2445_examples_weekly_on_t_and_th_for_5_weeks
282
+ # Weekly on Tuesday and Thursday for 5 weeks:
283
+ #
284
+ # DTSTART;TZID=US-Eastern:19970902T090000
285
+ # RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH
286
+ # or
287
+ #
288
+ # RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH
289
+ #
290
+ # ==> (1997 9:00 AM EDT)September 2,4,9,11,16,18,23,25,30;October 2
291
+ Test(
292
+ 'FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH',
293
+ '19970902T090000',
294
+ <<VEC
295
+ # ==> (1997 9:00 AM EDT)September 2,4,9,11,16,18,23,25,30;October 2
296
+ VEC
297
+ )
298
+ end
299
+
300
+ def test_rfc2445_examples_every_other_week_m_w_f_until_dec24
301
+ # Every other week on Monday, Wednesday and Friday until December 24,
302
+ # 1997, but starting on Tuesday, September 2, 1997:
303
+ #
304
+ # DTSTART;TZID=US-Eastern:19970902T090000
305
+ # RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;
306
+ # BYDAY=MO,WE,FR
307
+ # ==> (1997 9:00 AM EDT)September 2,3,5,15,17,19,29;October
308
+ # 1,3,13,15,17
309
+ # (1997 9:00 AM EST)October 27,29,31;November 10,12,14,24,26,28;
310
+ # December 8,10,12,22
311
+
312
+ Test(
313
+ 'FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR',
314
+ '19970902T090000',
315
+ <<VEC
316
+ # ==> (1997 9:00 AM EDT)September 2,3,5,15,17,19,29;October 1,3,13,15,17
317
+ # (1997 9:00 AM EST)October 27,29,31;November 10,12,14,24,26,28;December 8,10,12,22
318
+ VEC
319
+ )
320
+ end
321
+
322
+ def test_rfc2445_examples_every_other_week_t_th_for_8
323
+ # Every other week on Tuesday and Thursday, for 8 occurrences:
324
+ #
325
+ # DTSTART;TZID=US-Eastern:19970902T090000
326
+ # RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH
327
+ #
328
+ # ==> (1997 9:00 AM EDT)September 2,4,16,18,30;October 2,14,16
329
+ Test(
330
+ 'FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH',
331
+ '19970902T090000',
332
+ <<VEC
333
+ # ==> (1997 9:00 AM EDT)September 2,4,16,18,30;October 2,14,16
334
+ VEC
335
+ )
336
+ end
337
+
338
+ def test_rfc2445_examples_monthly
339
+ # Monthly on the 1st Friday for ten occurrences:
340
+ #
341
+ # DTSTART;TZID=US-Eastern:19970905T090000
342
+ # RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR
343
+ #
344
+ # ==> (1997 9:00 AM EDT)September 5;October 3
345
+ # (1997 9:00 AM EST)November 7;Dec 5
346
+ # (1998 9:00 AM EST)January 2;February 6;March 6;April 3
347
+ # (1998 9:00 AM EDT)May 1;June 5
348
+
349
+ Test(
350
+ 'FREQ=MONTHLY;COUNT=10;BYDAY=1FR',
351
+ '19970905T090000',
352
+ <<VEC
353
+ # ==> (1997 9:00 AM EDT)September 5;October 3
354
+ # (1997 9:00 AM EST)November 7;December 5
355
+ # (1998 9:00 AM EST)January 2;February 6;March 6;April 3
356
+ # (1998 9:00 AM EDT)May 1;June 5
357
+ VEC
358
+ )
359
+
360
+
361
+ # Monthly on the 1st Friday until December 24, 1997:
362
+ #
363
+ # DTSTART;TZID=US-Eastern:19970905T090000
364
+ # RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR
365
+ #
366
+ # ==> (1997 9:00 AM EDT)September 5;October 3
367
+ # (1997 9:00 AM EST)November 7;December 5
368
+
369
+ Test(
370
+ 'FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR',
371
+ '19970905T090000',
372
+ <<VEC
373
+ # ==> (1997 9:00 AM EDT)September 5;October 3
374
+ # (1997 9:00 AM EST)November 7;December 5
375
+ VEC
376
+ )
377
+
378
+ # Every other month on the 1st and last Sunday of the month for 10
379
+ # occurrences:
380
+ #
381
+ # DTSTART;TZID=US-Eastern:19970907T090000
382
+ # RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU
383
+ #
384
+ # ==> (1997 9:00 AM EDT)September 7,28
385
+ # (1997 9:00 AM EST)November 2,30
386
+ # (1998 9:00 AM EST)January 4,25;March 1,29
387
+ # (1998 9:00 AM EDT)May 3,31
388
+
389
+ Test(
390
+ 'FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU',
391
+ '19970907T090000',
392
+ <<VEC
393
+ # ==> (1997 9:00 AM EDT)September 7,28
394
+ # (1997 9:00 AM EST)November 2,30
395
+ # (1998 9:00 AM EST)January 4,25;March 1,29
396
+ # (1998 9:00 AM EDT)May 3,31
397
+ VEC
398
+ )
399
+
400
+ # Monthly on the second to last Monday of the month for 6 months:
401
+ #
402
+ # DTSTART;TZID=US-Eastern:19970922T090000
403
+ # RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO
404
+ #
405
+ # ==> (1997 9:00 AM EDT)September 22;October 20
406
+ # (1997 9:00 AM EST)November 17;December 22
407
+ # (1998 9:00 AM EST)January 19;February 16
408
+
409
+ Test(
410
+ 'FREQ=MONTHLY;COUNT=6;BYDAY=-2MO',
411
+ '19970922T090000',
412
+ <<VEC
413
+ # ==> (1997 9:00 AM EDT)September 22;October 20
414
+ # (1997 9:00 AM EST)November 17;December 22
415
+ # (1998 9:00 AM EST)January 19;February 16
416
+ VEC
417
+ )
418
+
419
+ # Monthly on the third to the last day of the month, forever:
420
+ #
421
+ # DTSTART;TZID=US-Eastern:19970928T090000
422
+ # RRULE:FREQ=MONTHLY;BYMONTHDAY=-3
423
+ #
424
+ # ==> (1997 9:00 AM EDT)September 28
425
+ # (1997 9:00 AM EST)October 29;November 28;December 29
426
+ # (1998 9:00 AM EST)January 29;February 26
427
+ # ...
428
+
429
+ Test(
430
+ 'FREQ=MONTHLY;BYMONTHDAY=-3;count=6',
431
+ '19970928T090000',
432
+ <<VEC
433
+ # ==> (1997 9:00 AM EDT)September 28
434
+ # (1997 9:00 AM EST)October 29;November 28;December 29
435
+ # (1998 9:00 AM EST)January 29;February 26
436
+ VEC
437
+ )
438
+
439
+ # Monthly on the 2nd and 15th of the month for 10 occurrences:
440
+ #
441
+ # DTSTART;TZID=US-Eastern:19970902T090000
442
+ # RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15
443
+ #
444
+ # ==> (1997 9:00 AM EDT)September 2,15;October 2,15
445
+ # (1997 9:00 AM EST)November 2,15;December 2,15
446
+ # (1998 9:00 AM EST)January 2,15
447
+
448
+ Test(
449
+ 'FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15',
450
+ '19970902T090000',
451
+ <<VEC
452
+ # ==> (1997 9:00 AM EDT)September 2,15;October 2,15
453
+ # (1997 9:00 AM EST)November 2,15;December 2,15
454
+ # (1998 9:00 AM EST)January 2,15
455
+ VEC
456
+ )
457
+
458
+ # Monthly on the first and last day of the month for 10 occurrences:
459
+ #
460
+ # DTSTART;TZID=US-Eastern:19970930T090000
461
+ # RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1
462
+ #
463
+ # ==> (1997 9:00 AM EDT)September 30;October 1
464
+ # (1997 9:00 AM EST)October 31;November 1,30;December 1,31
465
+ # (1998 9:00 AM EST)January 1,31;February 1
466
+
467
+ Test(
468
+ 'FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1',
469
+ '19970930T090000',
470
+ <<VEC
471
+ # ==> (1997 9:00 AM EDT)September 30;October 1
472
+ # (1997 9:00 AM EST)October 31;November 1,30;December 1,31
473
+ # (1998 9:00 AM EST)January 1,31;February 1
474
+ VEC
475
+ )
476
+
477
+ # Every 18 months on the 10th thru 15th of the month for 10
478
+ # occurrences:
479
+ #
480
+ # DTSTART;TZID=US-Eastern:19970910T090000
481
+ # RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,
482
+ # 15
483
+ #
484
+ # ==> (1997 9:00 AM EDT)September 10,11,12,13,14,15
485
+ # (1999 9:00 AM EST)March 10,11,12,13
486
+
487
+ Test(
488
+ 'FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15',
489
+ '19970910T090000',
490
+ <<VEC
491
+ # ==> (1997 9:00 AM EDT)September 10,11,12,13,14,15
492
+ # (1999 9:00 AM EST)March 10,11,12,13
493
+ VEC
494
+ )
495
+
496
+ # Every Tuesday, every other month:
497
+ #
498
+ # DTSTART;TZID=US-Eastern:19970902T090000
499
+ # RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU
500
+ #
501
+ # ==> (1997 9:00 AM EDT)September 2,9,16,23,30
502
+ # (1997 9:00 AM EST)November 4,11,18,25
503
+ # (1998 9:00 AM EST)January 6,13,20,27;March 3,10,17,24,31
504
+ # ...
505
+
506
+ Test(
507
+ 'FREQ=MONTHLY;INTERVAL=2;BYDAY=TU;count=18',
508
+ '19970902T090000',
509
+ <<VEC
510
+ # ==> (1997 9:00 AM EDT)September 2,9,16,23,30
511
+ # (1997 9:00 AM EST)November 4,11,18,25
512
+ # (1998 9:00 AM EST)January 6,13,20,27;March 3,10,17,24,31
513
+ VEC
514
+ )
515
+ end
516
+
517
+ def test_rfc2445_examples_misc1
518
+ # Yearly in June and July for 10 occurrences:
519
+ #
520
+ # DTSTART;TZID=US-Eastern:19970610T090000
521
+ # RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7
522
+ # ==> (1997 9:00 AM EDT)June 10;July 10
523
+ # (1998 9:00 AM EDT)June 10;July 10
524
+ # (1999 9:00 AM EDT)June 10;July 10
525
+ # (2000 9:00 AM EDT)June 10;July 10
526
+ # (2001 9:00 AM EDT)June 10;July 10
527
+ # Note: Since none of the BYDAY, BYMONTHDAY or BYYEARDAY components
528
+ # are specified, the day is gotten from DTSTART
529
+
530
+ Test(
531
+ 'FREQ=YEARLY;COUNT=10;BYMONTH=6,7',
532
+ '19970610T090000',
533
+ <<VEC
534
+ # ==> (1997 9:00 AM EDT)June 10;July 10
535
+ # (1998 9:00 AM EDT)June 10;July 10
536
+ # (1999 9:00 AM EDT)June 10;July 10
537
+ # (2000 9:00 AM EDT)June 10;July 10
538
+ # (2001 9:00 AM EDT)June 10;July 10
539
+ VEC
540
+ )
541
+
542
+ # Every other year on January, February, and March for 10 occurrences:
543
+ #
544
+ # DTSTART;TZID=US-Eastern:19970310T090000
545
+ # RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3
546
+ #
547
+ # ==> (1997 9:00 AM EST)March 10
548
+ # (1999 9:00 AM EST)January 10;February 10;March 10
549
+ # (2001 9:00 AM EST)January 10;February 10;March 10
550
+ # (2003 9:00 AM EST)January 10;February 10;March 10
551
+
552
+ Test(
553
+ 'FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3',
554
+ '19970310T090000',
555
+ <<VEC
556
+ # ==> (1997 9:00 AM EST)March 10
557
+ # (1999 9:00 AM EST)January 10;February 10;March 10
558
+ # (2001 9:00 AM EST)January 10;February 10;March 10
559
+ # (2003 9:00 AM EST)January 10;February 10;March 10
560
+ VEC
561
+ )
562
+
563
+ # Every 3rd year on the 1st, 100th and 200th day for 10 occurrences:
564
+ #
565
+ # DTSTART;TZID=US-Eastern:19970101T090000
566
+ # RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200
567
+ #
568
+ # ==> (1997 9:00 AM EST)January 1
569
+ # (1997 9:00 AM EDT)April 10;July 19
570
+ # (2000 9:00 AM EST)January 1
571
+ # (2000 9:00 AM EDT)April 9;July 18
572
+ # (2003 9:00 AM EST)January 1
573
+ # (2003 9:00 AM EDT)April 10;July 19
574
+ # (2006 9:00 AM EST)January 1
575
+
576
+ Test(
577
+ 'FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200',
578
+ '19970101T090000',
579
+ <<VEC
580
+ # ==> (1997 9:00 AM EST)January 1
581
+ # (1997 9:00 AM EDT)April 10;July 19
582
+ # (2000 9:00 AM EST)January 1
583
+ # (2000 9:00 AM EDT)April 9;July 18
584
+ # (2003 9:00 AM EST)January 1
585
+ # (2003 9:00 AM EDT)April 10;July 19
586
+ # (2006 9:00 AM EST)January 1
587
+ VEC
588
+ )
589
+
590
+ # Every 20th Monday of the year, forever:
591
+ # DTSTART;TZID=US-Eastern:19970519T090000
592
+ # RRULE:FREQ=YEARLY;BYDAY=20MO
593
+ #
594
+ # ==> (1997 9:00 AM EDT)May 19
595
+ # (1998 9:00 AM EDT)May 18
596
+ # (1999 9:00 AM EDT)May 17
597
+ # ...
598
+
599
+ Test(
600
+ 'FREQ=YEARLY;BYDAY=20MO;count=3',
601
+ '19970519T090000',
602
+ <<VEC
603
+ # ==> (1997 9:00 AM EDT)May 19
604
+ # (1998 9:00 AM EDT)May 18
605
+ # (1999 9:00 AM EDT)May 17
606
+ VEC
607
+ )
608
+
609
+ # Monday of week number 20 (where the default start of the week is
610
+ # Monday), forever:
611
+ #
612
+ # DTSTART;TZID=US-Eastern:19970512T090000
613
+ # RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO
614
+ #
615
+ # ==> (1997 9:00 AM EDT)May 12
616
+ # (1998 9:00 AM EDT)May 11
617
+ # (1999 9:00 AM EDT)May 17
618
+ # ...
619
+
620
+ # TODO
621
+
622
+ # Every Thursday in March, forever:
623
+ #
624
+ # DTSTART;TZID=US-Eastern:19970313T090000
625
+ # RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH
626
+ #
627
+ # ==> (1997 9:00 AM EST)March 13,20,27
628
+ # (1998 9:00 AM EST)March 5,12,19,26
629
+ # (1999 9:00 AM EST)March 4,11,18,25
630
+ # ...
631
+
632
+ Test(
633
+ 'FREQ=YEARLY;BYMONTH=3;BYDAY=TH;count=11',
634
+ '19970313T090000',
635
+ <<VEC
636
+ # ==> (1997 9:00 AM EST)March 13,20,27
637
+ # (1998 9:00 AM EST)March 5,12,19,26
638
+ # (1999 9:00 AM EST)March 4,11,18,25
639
+ VEC
640
+ )
641
+
642
+ # Every Thursday, but only during June, July, and August, forever:
643
+ #
644
+ # DTSTART;TZID=US-Eastern:19970605T090000
645
+ # RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8
646
+ #
647
+ # ==> (1997 9:00 AM EDT)June 5,12,19,26;July 3,10,17,24,31;
648
+ # August 7,14,21,28
649
+ # (1998 9:00 AM EDT)June 4,11,18,25;July 2,9,16,23,30;
650
+ # August 6,13,20,27
651
+ # (1999 9:00 AM EDT)June 3,10,17,24;July 1,8,15,22,29;
652
+ # August 5,12,19,26
653
+ # ...
654
+
655
+ Test(
656
+ 'FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8;count=39',
657
+ '19970605T090000',
658
+ <<VEC
659
+ # ==> (1997 9:00 AM EDT)June 5,12,19,26;July 3,10,17,24,31;August 7,14,21,28
660
+ # (1998 9:00 AM EDT)June 4,11,18,25;July 2,9,16,23,30;August 6,13,20,27
661
+ # (1999 9:00 AM EDT)June 3,10,17,24;July 1,8,15,22,29;August 5,12,19,26
662
+ VEC
663
+ )
664
+
665
+ =begin
666
+
667
+ EXDATE isn't supported.
668
+
669
+ # Every Friday the 13th, forever:
670
+ #
671
+ # DTSTART;TZID=US-Eastern:19970902T090000
672
+ # EXDATE;TZID=US-Eastern:19970902T090000
673
+ # RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13
674
+ # ==> (1998 9:00 AM EST)February 13;March 13;November 13
675
+ # (1999 9:00 AM EDT)August 13
676
+ # (2000 9:00 AM EDT)October 13
677
+ # ...
678
+ Test(
679
+ 'FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13;count=5',
680
+ '19970902T090000',
681
+ <<VEC
682
+ # ==> (1998 9:00 AM EST)February 13;March 13;November 13
683
+ # (1999 9:00 AM EDT)August 13
684
+ # (2000 9:00 AM EDT)October 13
685
+ VEC
686
+ )
687
+ =end
688
+
689
+ # The first Saturday that follows the first Sunday of the month,
690
+ # forever:
691
+ #
692
+ # DTSTART;TZID=US-Eastern:19970913T090000
693
+ # RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13
694
+ #
695
+ # ==> (1997 9:00 AM EDT)September 13;October 11
696
+ # (1997 9:00 AM EST)November 8;December 13
697
+ # (1998 9:00 AM EST)January 10;February 7;March 7
698
+ # (1998 9:00 AM EDT)April 11;May 9;June 13...
699
+ # ...
700
+
701
+ Test(
702
+ 'FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13;count=10',
703
+ '19970913T090000',
704
+ <<VEC
705
+ # ==> (1997 9:00 AM EDT)September 13;October 11
706
+ # (1997 9:00 AM EST)November 8;December 13
707
+ # (1998 9:00 AM EST)January 10;February 7;March 7
708
+ # (1998 9:00 AM EDT)April 11;May 9;June 13...
709
+ VEC
710
+ )
711
+
712
+ # Every four years, the first Tuesday after a Monday in November,
713
+ # forever (U.S. Presidential Election day):
714
+ #
715
+ # DTSTART;TZID=US-Eastern:19961105T090000
716
+ # RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,
717
+ # 5,6,7,8
718
+ #
719
+ # ==> (1996 9:00 AM EST)November 5
720
+ # (2000 9:00 AM EST)November 7
721
+ # (2004 9:00 AM EST)November 2
722
+ # ...
723
+
724
+ Test(
725
+ 'FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8;count=3',
726
+ '19961105T090000',
727
+ <<VEC
728
+ # ==> (1996 9:00 AM EST)November 5
729
+ # (2000 9:00 AM EST)November 7
730
+ # (2004 9:00 AM EST)November 2
731
+ VEC
732
+ )
733
+
734
+ # The 3rd instance into the month of one of Tuesday, Wednesday or
735
+ # Thursday, for the next 3 months:
736
+ #
737
+ # DTSTART;TZID=US-Eastern:19970904T090000
738
+ # RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3
739
+ #
740
+ # ==> (1997 9:00 AM EDT)September 4;October 7
741
+ # (1997 9:00 AM EST)November 6
742
+
743
+ Test(
744
+ 'FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3',
745
+ '19970904T090000',
746
+ <<VEC
747
+ # ==> (1997 9:00 AM EDT)September 4;October 7
748
+ # (1997 9:00 AM EST)November 6
749
+ VEC
750
+ )
751
+
752
+ end
753
+
754
+ def test_rfc2445_example_2nd_last_weekday_of_month
755
+ # The 2nd to last weekday of the month:
756
+ #
757
+ # DTSTART;TZID=US-Eastern:19970929T090000
758
+ # RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2
759
+ #
760
+ # ==> (1997 9:00 AM EDT)September 29
761
+ # (1997 9:00 AM EST)October 30;November 27;December 30
762
+ # (1998 9:00 AM EST)January 29;February 26;March 30
763
+ # ...
764
+ #
765
+
766
+ Test(
767
+ 'FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2;count=7',
768
+ '19970929T090000',
769
+ <<VEC
770
+ # ==> (1997 9:00 AM EDT)September 29
771
+ # (1997 9:00 AM EST)October 30;November 27;December 30
772
+ # (1998 9:00 AM EST)January 29;February 26;March 30
773
+ VEC
774
+ )
775
+ end
776
+
777
+ def test_rfc2445_examples_misc2
778
+ # Every 3 hours from 9:00 AM to 5:00 PM on a specific day:
779
+ #
780
+ # DTSTART;TZID=US-Eastern:19970902T090000
781
+ # RRULE:FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z
782
+ #
783
+ # ==> (September 2, 1997 EDT)09:00,12:00,15:00
784
+ #
785
+ # Every 15 minutes for 6 occurrences:
786
+ #
787
+ # DTSTART;TZID=US-Eastern:19970902T090000
788
+ # RRULE:FREQ=MINUTELY;INTERVAL=15;COUNT=6
789
+ #
790
+ # ==> (September 2, 1997 EDT)09:00,09:15,09:30,09:45,10:00,10:15
791
+ #
792
+ # Every hour and a half for 4 occurrences:
793
+ #
794
+ # DTSTART;TZID=US-Eastern:19970902T090000
795
+ # RRULE:FREQ=MINUTELY;INTERVAL=90;COUNT=4
796
+ #
797
+ # ==> (September 2, 1997 EDT)09:00,10:30;12:00;13:30
798
+ #
799
+ # Every 20 minutes from 9:00 AM to 4:40 PM every day:
800
+ #
801
+ # DTSTART;TZID=US-Eastern:19970902T090000
802
+ # RRULE:FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40
803
+ # or
804
+ # RRULE:FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16
805
+ #
806
+ # ==> (September 2, 1997 EDT)9:00,9:20,9:40,10:00,10:20,
807
+ # ... 16:00,16:20,16:40
808
+ # (September 3, 1997 EDT)9:00,9:20,9:40,10:00,10:20,
809
+ # ...16:00,16:20,16:40
810
+ # ...
811
+
812
+ end
813
+
814
+ def test_rfc2445_examples_weekly_days_differ_on_wkst
815
+ # An example where the days generated makes a difference because of
816
+ # WKST:
817
+ #
818
+ # DTSTART;TZID=US-Eastern:19970805T090000
819
+ # RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO
820
+ #
821
+ # ==> (1997 EDT)Aug 5,10,19,24
822
+ Test(
823
+ 'FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO',
824
+ '19970805T090000',
825
+ <<VEC
826
+ # ==> (1997 9:00 AM EDT)August 5,10,19,24
827
+ VEC
828
+ )
829
+
830
+ #
831
+ # changing only WKST from MO to SU, yields different results...
832
+ #
833
+ # DTSTART;TZID=US-Eastern:19970805T090000
834
+ # RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU
835
+ # ==> (1997 EDT)August 5,17,19,31
836
+
837
+ Test(
838
+ 'FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU',
839
+ '19970805T090000',
840
+ <<VEC
841
+ # ==> (1997 9:00 AM EDT)August 5,17,19,31
842
+ VEC
843
+ )
844
+ end
845
+
846
+ def test_us_laborday
847
+ =begin
848
+ Patch with test from Sam Stephenson at 37signals:
849
+
850
+ We're using your vPim library at 37signals for the Backpack Calendar
851
+ (http://backpackit.com/calendar ) and ran into an issue with the "US
852
+ Holidays" calendar available at http://ical.mac.com/ical/US32Holidays.ics .
853
+ Specifically, calling Vpim::Rrule#each for events such as Labor Day:
854
+
855
+ DTSTART;VALUE=DATE:20020902
856
+ DTEND;VALUE=DATE:20020903
857
+ RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=9;BYDAY=1MO
858
+
859
+ would never yield any recurrences.
860
+ =end
861
+
862
+ # The first Monday in September, forever (Labor Day):
863
+ #
864
+ # DTSTART;TZID=US-Eastern:20020902T090000
865
+ # RRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH=9;BYDAY=1MO
866
+ #
867
+ # ==> (2002 9:00 AM EST)September 2
868
+ # (2003 9:00 AM EST)September 1
869
+ # (2004 9:00 AM EST)September 6
870
+ # ...
871
+
872
+ Test(
873
+ 'FREQ=YEARLY;INTERVAL=1;BYMONTH=9;BYDAY=1MO;count=3',
874
+ '20020902T090000',
875
+ <<VEC
876
+ # ==> (2002 9:00 AM EDT)September 2
877
+ # (2003 9:00 AM EDT)September 1
878
+ # (2004 9:00 AM EDT)September 6
879
+ VEC
880
+ )
881
+ end
882
+
883
+ def test_zipdx_weekly_1
884
+ # Example provided by Zipdx
885
+ # Produced by: Zimbra-Calendar-Provider
886
+ # Interop tested against Apple iCal 3.0.2
887
+ Test(
888
+ 'FREQ=WEEKLY;UNTIL=20080501;INTERVAL=1;BYDAY=TU,TH',
889
+ '20080415T160000',
890
+ <<VEC
891
+ # ==> (2008 4:00 PM EDT)April 15,17,22,24,29
892
+ VEC
893
+ );
894
+ end
895
+
896
+ def test_zipdx_weekly_2
897
+ # Example provided by Zipdx
898
+ # Produced by: -//Microsoft Corporation//Outlook 11.0 MIMEDIR//EN
899
+ # Interop tested against Apple iCal 3.0.2
900
+ Test(
901
+ 'FREQ=WEEKLY;COUNT=9;INTERVAL=2;BYDAY=MO,WE,FR;WKST=SU',
902
+ '20080811T130000',
903
+ <<VEC
904
+ # ==> (2008 1:00 PM EDT)August 11,13,15,25,27,29;September 8,10,12
905
+ VEC
906
+ );
907
+ end
908
+
909
+ def test_zipdx_daily_1
910
+ # Example provided by Zipdx
911
+ # Produced by: Microsoft CDO for Microsoft Exchange
912
+ # Interop tested against Apple iCal 3.0.2
913
+ Test(
914
+ 'FREQ=DAILY;COUNT=7;WKST=SU;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR',
915
+ '20080218T180000',
916
+ <<VEC
917
+ # ==> (2008 6:00 PM EST)February 18,19,20,21,22,25,26
918
+ VEC
919
+ );
920
+ end
921
+
922
+ def test_bysetpos_before_dtstart
923
+ # Note - this doesn't work with Apple iCal 3.0.2, I think its their bug.
924
+ Test(
925
+ 'FREQ=MONTHLY;COUNT=5;BYDAY=MO;BYSETPOS=1,2',
926
+ '20080305T180000',
927
+ <<VEC
928
+ # ==> (2008 6:00 PM EST)March 5
929
+ # (2008 6:00 PM EDT)March 10
930
+ # (2008 6:00 PM EDT)April 7,14
931
+ # (2008 6:00 PM EDT)May 5
932
+ VEC
933
+ );
934
+ end
935
+
936
+ def test_bysetpos_after_until
937
+ Test(
938
+ 'FREQ=MONTHLY;UNTIL=20080421;BYDAY=MO;BYSETPOS=-1',
939
+ '20080305T180000',
940
+ <<VEC
941
+ # ==> (2008 6:00 PM EST)March 5
942
+ # (2008 6:00 PM EDT)March 31
943
+ VEC
944
+ );
945
+ end
946
+
947
+ def test_bysetpos_zipdx_last_saturday
948
+ # In Microsoft Exchange, if I want a meeting to occur on a certain Saturday
949
+ # of each month, Exchange generates:
950
+ #
951
+ # RRULE:FREQ=MONTHLY;COUNT=4;WKST=SU;INTERVAL=1;BYDAY=-1SA
952
+ #
953
+ # However, if I use Microsoft Outlook, Outlook generates:
954
+ # RRULE:FREQ=MONTHLY;COUNT=4;INTERVAL=1;BYDAY=SA;BYSETPOS=-1;WKST=SU
955
+ # And we get confused and generate the meeting every week (instead of every
956
+ # month). I think this is because the library does not support BYSETPOS;
957
+ # this is stated in the documentation.
958
+ Test(
959
+ 'FREQ=MONTHLY;COUNT=4;INTERVAL=1;WKST=SU;BYDAY=SA;BYSETPOS=-1',
960
+ '20080305T180000',
961
+ <<VEC
962
+ # ==> (2008 6:00 PM EST)March 5
963
+ # (2008 6:00 PM EDT)March 29
964
+ # (2008 6:00 PM EDT)April 26
965
+ # (2008 6:00 PM EDT)May 31
966
+ VEC
967
+ );
968
+ end
969
+ =begin
970
+ BEGIN:VEVENT
971
+ SUMMARY:Boxing Day
972
+ DESCRIPTION:First Weekday on or after December 26th.
973
+ DTSTAMP:20030701T000000Z
974
+ UID:holiday0042@icaldates.com
975
+ CATEGORIES:Holiday - Canada
976
+ DTSTART;VALUE=DATE:17531226
977
+ RRULE:FREQ=MONTHLY;BYMONTH=12;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR;BYMONTHDAY=26,27,28;BYSETPOS=1
978
+ END:VEVENT
979
+ =end
980
+
981
+
982
+ def test_reccurrence_with_utc_dtstart
983
+ # Its wrong that the times yielded aren't in the timezone of DTSTART, but
984
+ # until vPim supports timezones, its the best it'll get.
985
+ txt = <<'__'
986
+ BEGIN:VCALENDAR
987
+ BEGIN:VEVENT
988
+ DTSTAMP:20080416T174954Z
989
+ ORGANIZER;CN=Anonymous:MAILTO:ano@nymo.us
990
+ CREATED:20080401T090904Z
991
+ LAST-MODIFIED:20080401T090904Z
992
+ SUMMARY:Very important recurring event
993
+ RRULE:FREQ=WEEKLY;UNTIL=20080415T093000Z;BYDAY=TU;BYHOUR=9
994
+ DTSTART:20080401T093000Z
995
+ DTEND:20080401T110000Z
996
+ TRANSP:OPAQUE
997
+ END:VEVENT
998
+ END:VCALENDAR
999
+ __
1000
+ cal = Vpim::Icalendar.decode(txt).first
1001
+ occurs = cal.events.to_a.first.occurrences.to_a
1002
+ #p occurs
1003
+ utc = occurs.map{|y| y.utc}
1004
+ #p utc
1005
+ expects = [
1006
+ Time.utc(2008, 4, 1, 9, 30),
1007
+ Time.utc(2008, 4, 8, 9, 30),
1008
+ Time.utc(2008, 4,15, 9, 30),
1009
+ ]
1010
+ assert_equal(expects, utc)
1011
+ end
1012
+
1013
+ def test_maker
1014
+ assert_equal("FREQ=WEEKLY",
1015
+ Rrule::Maker.new{|m|m.frequency = "WEEKLY"}.encode)
1016
+ assert_equal("FREQ=WEEKLY;COUNT=2",
1017
+ Rrule::Maker.new{|m|m.frequency = "WEEKLY"; m.count = 2}.encode)
1018
+ assert_raises(ArgumentError) do
1019
+ Rrule::Maker.new{|m|m.count = 2; m.until = Time.now}
1020
+ end
1021
+ assert_raises(ArgumentError) do
1022
+ Rrule::Maker.new{|m|m.until = Time.now; m.count = 4}
1023
+ end
1024
+ assert_raises(ArgumentError) do
1025
+ Rrule::Maker.new.encode
1026
+ end
1027
+ end
1028
+
1029
+ end
1030
+