Skip to content

Commit f7c4385

Browse files
committed
FREQ=weekly support
1 parent 9f9dfbf commit f7c4385

23 files changed

+2298
-566
lines changed

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
SHELL:=/bin/sh
44

5-
RUBY=/opt/local/bin/ruby
5+
RUBY=/usr/bin/ruby
66

77
.PHONY: default doc test other
88

@@ -118,7 +118,7 @@ stamp:
118118

119119
gem:
120120
mkdir -p releases
121-
ruby18 vpim.gemspec
121+
ruby vpim.gemspec
122122
mv vpim-$V.gem releases/
123123

124124
pkg:
@@ -131,7 +131,7 @@ pkg:
131131
mkdir -p $R/samples
132132
mkdir -p $R/test
133133
mkdir -p $R/etc
134-
cp COPYING README CHANGES install.rb $R/
134+
cp COPYING README CHANGES setup.rb $R/
135135
cp lib/*.rb $R/lib/
136136
cp lib/vpim/*.rb $R/lib/vpim/
137137
cp lib/vpim/maker/*.rb $R/lib/vpim/maker/

TODO

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@ Output a graph of the connections in address book, spouses, etc.
44

55
http://serveringhaus.org/projects/icv - ical validator
66

7+
http://wiki.mozilla.org/Calendar:QA_Test_Servers
78

89
ical2 support
10+
ical3 support
11+
12+
http://www.kanzaki.com/docs/ical/
913

1014
vpim rrule encoding APIs
1115

lib/vpim/CVS/Entries

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ D/property////
1010
/icalendar.rb/1.1/Sat Mar 10 01:16:32 2007//
1111
/rfc2425.rb/1.2/Tue Oct 30 05:55:54 2007//
1212
/rrule.rb/1.2/Fri Sep 21 03:10:53 2007//
13-
/time.rb/1.1/Sat Mar 10 01:16:32 2007//
1413
/vcard.rb/1.1/Sat Mar 10 01:16:32 2007//
1514
/version.rb/1.1/Sat Feb 16 22:27:14 2008//
1615
/vevent.rb/1.1/Sat Mar 10 01:16:32 2007//
1716
/vjournal.rb/1.1/Sat Mar 10 01:16:32 2007//
1817
/vpim.rb/1.1/Sat Mar 10 01:16:32 2007//
1918
/vtodo.rb/1.2/Wed Oct 24 03:24:22 2007//
19+
/time.rb/1.1/Sun Feb 17 22:10:04 2008//

lib/vpim/date.rb

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ def Date.str2wday(wdaystr)
7777
# - Date.bywday(2004, -2, 3, -2) => second last Wednesday in the second last month of 2004
7878
#
7979
# Compare this to Date.new, which allows a Date to be created by
80-
# day-of-the-month, mday, to Date.new2, which allows a Date to be created by
81-
# day-of-the-year, yday, and to Date.neww, which allows a Date to be created
80+
# day-of-the-month, mday, to Date.ordinal, which allows a Date to be created by
81+
# day-of-the-year, yday, and to Date.commercial, which allows a Date to be created
8282
# by day-of-the-week, but within a specific week.
8383
def Date.bywday(year, mon, wday, n = 1, sg=Date::ITALY)
8484
# Normalize mon to 1-12.
@@ -120,10 +120,36 @@ def Date.bywday(year, mon, wday, n = 1, sg=Date::ITALY)
120120
end
121121
d
122122
end
123+
124+
# Return the first day of the week for the specified date. Commercial weeks
125+
# start on Monday, but the weekstart can be specified (as 0-6, where 0 is
126+
# sunday, or in formate of Date.str2day).
127+
def Date.weekstart(year, mon, day, weekstart="MO")
128+
wkst = Date.str2wday(weekstart)
129+
d = Date.new(year, mon, day)
130+
until d.wday == wkst
131+
d = d - 1
132+
end
133+
d
134+
end
123135
end
124136

125137
# DateGen generates arrays of dates matching simple criteria.
126138
class DateGen
139+
140+
# Generate an array of a week's dates, where week is specified by year, mon,
141+
# day, and the weekstart (the day-of-week that is considered the "first" day
142+
# of that week, 0-6, where 0 is sunday).
143+
def DateGen.weekofdate(year, mon, day, weekstart)
144+
d = Date.weekstart(year, mon, day, weekstart)
145+
week = []
146+
7.times do
147+
week << d
148+
d = d + 1
149+
end
150+
week
151+
end
152+
127153
# Generate an array of dates on +wday+ (the day-of-week,
128154
# 0-6, where 0 is Sunday).
129155
#

lib/vpim/rrule.rb

Lines changed: 74 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
1717
$debug = ENV['DEBUG']
1818
19+
class Date
20+
def inspect
21+
self.to_s
22+
end
23+
end
24+
1925
def debug(*objs)
2026
if $debug
2127
pp(*objs)
@@ -30,14 +36,14 @@ module Vpim
3036
# syntax description and examples from RFC 2445. The description is pretty
3137
# hard to understand, but the examples are more helpful.
3238
#
33-
# The implementation is pretty complete, but still lacks support for:
39+
# The implementation is reasonably complete, but still lacks support for:
3440
#
35-
# TODO - BYWEEKLY, BYWEEKNO, WKST: rules that recur by the week, or are
36-
# limited to particular weeks, not hard, but not trivial, I'll do it for the
37-
# next release
41+
# Recurrence by date (RDATE) and exclusions (EXDATE, EXRULE).
42+
#
43+
# TODO - BYWEEKNO: rules that are limited to particular weeks in a year.
3844
#
3945
# TODO - BYHOUR, BYMINUTE, BYSECOND: trivial to do, but I don't have an
40-
# immediate need for them, I'll do it for the next release
46+
# immediate need for them.
4147
#
4248
# TODO - BYSETPOS: limiting to only certain recurrences in a set (what does
4349
# -1, last occurence, mean for an infinitely occuring rule?)
@@ -139,16 +145,18 @@ def each_until(dountil)
139145
# most systems.
140146
def each(dountil = nil) #:yield: ytime
141147
t = @dtstart.clone
142-
count = 1
143148

144149
# Time.to_a => [ sec, min, hour, day, month, year, wday, yday, isdst, zone ]
145150

146-
# Every event occurs at least once, at its start time, but only if the start
147-
# time is earlier than DOUNTIL...
151+
# Every event occurs at its start time, but only if the start time is
152+
# earlier than DOUNTIL...
153+
if !dountil || t < dountil
154+
yield t
155+
end
156+
count = 1
157+
158+
# With no recurrence, DTSTART is the only occurence.
148159
if !@rrule
149-
if !dountil || t < dountil
150-
yield t
151-
end
152160
return self
153161
end
154162

@@ -168,20 +176,21 @@ def each(dountil = nil) #:yield: ytime
168176
#when 'YEARLY' then
169177
# Don't need to keep track of year, all occurences are within t's
170178
# year.
171-
when 'MONTHLY' then days.month = t.month #month = { t.month => nil }
172-
# when 'WEEKLY' then days.mday = t.month, t.mday
179+
when 'MONTHLY' then days.month = t.month
180+
when 'WEEKLY' then #days.month = t.month
173181
# TODO - WEEKLY
174-
when 'DAILY' then days.mday = t.month, t.mday #month = { t.month => [ t.mday ] }
175-
when 'HOURLY' then hour = [t.hour]
176-
when 'MINUTELY' then min = [t.min]
177-
when 'SECONDLY' then sec = [t.sec]
182+
when 'DAILY' then days.mday = t.month, t.mday
183+
when 'HOURLY' then hour = [t.hour]
184+
when 'MINUTELY' then min = [t.min]
185+
when 'SECONDLY' then sec = [t.sec]
178186
end
179187

188+
# debug [t, days]
180189
# Process the BY* modifiers in RFC defined order:
181190
# BYMONTH, BYWEEKNO, BYYEARDAY, BYMONTHDAY, BYDAY,
182191
# BYHOUR, BYMINUTE, BYSECOND and BYSETPOS
183-
184-
bymon = [nil]
192+
193+
bymon = [nil]
185194

186195
if @by['BYMONTH']
187196
bymon = @by['BYMONTH'].split(',')
@@ -220,7 +229,6 @@ def each(dountil = nil) #:yield: ytime
220229

221230
if @by['BYDAY']
222231
byday = @by['BYDAY'].scan(/,?([+-]?[1-9]?\d*)?(SU|MO|TU|WE|TH|FR|SA)/i)
223-
# debug byday
224232

225233
# BYDAY means different things in different frequencies. The +n+
226234
# is only meaningful when freq is yearly or monthly.
@@ -231,7 +239,7 @@ def each(dountil = nil) #:yield: ytime
231239
when 'MONTHLY'
232240
dates = byday_in_monthly(t.year, t.month, byday)
233241
when 'WEEKLY'
234-
# dates = byday_in_weekly(t.year, wkstart, t.month, t.day, byday)
242+
dates = byday_in_weekly(t.year, t.month, t.mday, @wkst, byday)
235243
when 'DAILY', 'HOURLY', 'MINUTELY', 'SECONDLY'
236244
# Reuse the byday_in_monthly. Current day is already specified,
237245
# so this will just eliminate the current day if its not allowed
@@ -248,29 +256,34 @@ def each(dountil = nil) #:yield: ytime
248256

249257
# TODO - BYSETPOS
250258

251-
# Yield the time, if we haven't gone over COUNT, or past UNTIL, or past
252-
# the end of representable time.
253-
254259
hour = [@dtstart.hour] if !hour
255260
min = [@dtstart.min] if !min
256261
sec = [@dtstart.sec] if !sec
257262

258263
# debug days
259264

265+
# Yield the time, if we haven't gone over COUNT, or past UNTIL, or past
266+
# the end of representable time.
267+
260268
days.each do |m,d|
261269
hour.each do |h|
262270
min.each do |n|
263271
sec.each do |s|
264-
if(@count && (count > @count))
265-
return self
266-
end
267272
y = Time.local(t.year, m, d, h, n, s, 0)
268273

269274
next if y.hour != h
270275

271276
# The generated set can sometimes generate results earlier
272-
# than the DTSTART, skip them.
273-
next if y < @dtstart
277+
# than the DTSTART, skip them. Also, we already yielded
278+
# DTSTART, skip it.
279+
next if y <= @dtstart
280+
281+
count += 1
282+
283+
# We are done if current count is past @count.
284+
if(@count && (count > @count))
285+
return self
286+
end
274287

275288
# We are done if current time is past @until.
276289
if @until && (y > @until)
@@ -282,7 +295,6 @@ def each(dountil = nil) #:yield: ytime
282295
return self
283296
end
284297
yield y
285-
count += 1
286298
end
287299
end
288300
end
@@ -363,30 +375,30 @@ def intersect_bymon(bymon) #:nodoc:
363375
end
364376

365377
def intersect_dates(dates) #:nodoc:
366-
if dates
367-
# If no months are in the dayset, add all the ones in dates
368-
if !@month
369-
@month = {}
378+
return unless dates
370379

371-
dates.each do |d|
372-
@month[d.mon] = nil
373-
end
380+
# If no months are in the dayset, add all the ones in dates
381+
if !@month
382+
@month = {}
383+
384+
dates.each do |d|
385+
@month[d.mon] = nil
374386
end
387+
end
375388

376-
# In each month,
377-
# if there are days,
378-
# eliminate those not in dates
379-
# otherwise
380-
# add all those in dates
381-
@month.each do |mon, days|
382-
days_in_mon = dates.find_all { |d| d.mon == mon }
383-
days_in_mon = days_in_mon.collect { |d| d.day }
384-
385-
if days
386-
days_in_mon = days_in_mon & days
387-
end
388-
@month[mon] = days_in_mon
389+
# In each month,
390+
# if there are days,
391+
# eliminate those not in dates
392+
# otherwise
393+
# add all those in dates
394+
@month.each do |mon, days|
395+
days_in_mon = dates.find_all { |d| d.mon == mon }
396+
days_in_mon = days_in_mon.collect { |d| d.day }
397+
398+
if days
399+
days_in_mon = days_in_mon & days
389400
end
401+
@month[mon] = days_in_mon
390402
end
391403
end
392404

@@ -451,7 +463,7 @@ def byyearday(year, byyday) #:nodoc:
451463
dates = []
452464

453465
byyday.each do |yday|
454-
dates << Date.new2(year, yday[0].to_i)
466+
dates << Date.ordinal(year, yday[0].to_i)
455467
end
456468
dates.sort!
457469
dates
@@ -472,6 +484,17 @@ def byday_in_monthly(year, mon, byday) #:nodoc:
472484
dates
473485
end
474486

487+
def byday_in_weekly(year, mon, day, wkst, byday)
488+
# debug ["day", year,mon,day,wkst,byday]
489+
days = byday.map{ |_, byday| Date.str2wday(byday) }
490+
week = DateGen.weekofdate(year, mon, day, wkst)
491+
# debug [ "week", dates ]
492+
week.delete_if do |d|
493+
!days.include?(d.wday)
494+
end
495+
week
496+
end
497+
475498
end
476499

477500
end

mbox2vcard.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#!/usr/bin/env ruby
22

3+
$-w = true
4+
35
$:.unshift File.dirname($0)
46

57
require 'rmail'

samples/ab-query.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env ruby
22

3-
$:.unshift File.dirname($0) + '/lib'
3+
$-w = true
4+
$:.unshift File.dirname($0) + '/../lib'
45

56
require 'osx-wrappers'
67

samples/cmd-itip.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env ruby
22

3-
$:.unshift File.dirname($0)
3+
$-w = true
4+
$:.unshift File.dirname($0) + '/../lib'
45

56
require 'getoptlong'
67

@@ -109,9 +110,9 @@
109110
puts e.description
110111
end
111112

112-
if e.comment
113+
if e.comments
113114
puts finish="-- Comment --"
114-
puts " comment=#{e.comment}"
115+
puts " comment=#{e.comments}"
115116
end
116117

117118
if e.attendees.first

samples/ics-dump.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
#
33
# Calendars are in ~/Library/Calendars/
44

5-
$:.unshift File.dirname($0) + "/lib"
5+
$-w = true
6+
$:.unshift File.dirname($0) + '/../lib'
67

78
require 'getoptlong'
89
require 'pp'

samples/mutt-aliases-to-vcf.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env ruby
22

3-
$:.unshift File.dirname($0)
3+
$-w = true
4+
$:.unshift File.dirname($0) + '/../lib'
45

56
require 'vpim/vcard'
67

0 commit comments

Comments
 (0)