Thinking about a date-matching algorithm

I did a little swapping of ideas with dblack on this. Now I’m
opening discussion to anyone interested.

I’m writing a little to-do manager (chiefly for my own use).

I want to allow recurring tasks. Some of these will be simple, like
"Every Monday." Others will be more complex, like “Every 2nd and 4th
Friday.” Some might not even be based on weeks or months at all, but
might be like: “Every ten days, no matter what.”

There’d also be an option to give advance warning (N days) on each
event.

So the question becomes: Given a date (typically “today”) and a list
of recurring tasks, how do I determine which ones need to be displayed?

Cheers,
Hal

I don’t get it. Are you asking for something along the lines of Perl’s date-parser in Date::Manip?
If so, then I think you should have a look at the source(s) of the relevant perl functions / methods in Date::Manip.

See also

http://search.cpan.org/search?query=date-manip&mode=all
http://dates.rcbowen.com/

···

On Thu, 18 Mar 2004 15:40:06 +0900, Hal Fulton hal9000@hypermetrics.com wrote:

So the question becomes: Given a date (typically “today”) and a list
of recurring tasks, how do I determine which ones need to be displayed?

“Hal Fulton” hal9000@hypermetrics.com schrieb im Newsbeitrag
news:40594440.3000202@hypermetrics.com

I did a little swapping of ideas with dblack on this. Now I’m
opening discussion to anyone interested.

I’m writing a little to-do manager (chiefly for my own use).

I want to allow recurring tasks. Some of these will be simple, like
“Every Monday.” Others will be more complex, like “Every 2nd and 4th
Friday.” Some might not even be based on weeks or months at all, but
might be like: “Every ten days, no matter what.”

There’d also be an option to give advance warning (N days) on each
event.

So the question becomes: Given a date (typically “today”) and a list
of recurring tasks, how do I determine which ones need to be displayed?

How about:

class Schedule < Hash
def ===(time)
each do |sym, val|
case val
when Enumerable
return false unless val.include?( time.send(sym) )
when nil
# ignore
else
return false unless val == time.send(sym)
end
end

true

end
end

irb(main):017:0> sched = Schedule.new
=> {}
irb(main):018:0> sched[:year]=[2003,2004]
=> [2003, 2004]
irb(main):019:0>
irb(main):020:0* sched === Time.now
=> true
irb(main):021:0>
irb(main):022:0* sched[:year]=nil
=> nil
irb(main):023:0> sched === Time.now
=> true
irb(main):024:0>
irb(main):025:0* sched[:year]=[2005]
=> [2005]
irb(main):026:0> sched === Time.now
=> false
irb(main):029:0> sched[:year]=(2000…2010)
=> 2000…2010
irb(main):030:0> sched === Time.now
=> true

Regards

robert

Not sure if this is the sort of thing you’re looking for, but I would
probably start by reducing the task description to a set of rules which
need to be fulfilled (and which can be simply matched as true or false)

For each task you’d store when it was first run etc, and to work out
whether a task was due for a particular day by comparing the rules with
hypothetical values for that day.

e.g.
Every Monday
wday = 1

Every day
# Always match

Every second and fourth Friday
(wday = 5) & (weekday_occurrence_in_month = 2 |
weekday_occurrence_in_month = 4)

Every ten days
firstrun_days_ago % 10 # ‘%’ true if lhs multiple of rhs

The main advantage (IMHO) of using a dialect like this is that if it is
understood by your ruby program, you’ve got a reasonable chance of
being able to convert it to both relatively trust-able ruby code and
(potentially) a more humanly readable description.

Which could be stored in, for example, an XML file:

... <rules match="any"

(And it would probably be best to allow nesting of tags so that
you can combine match=“all” and match=“any”.

The rules and match tags could map fairly directly to classes which
could then check their rule or list of rules recursively.

And all you’d then need to do is pass a hash of values for the
particular day to the outermost rules object.

e.g.

date= Date.today
firstrun = Date.parse ‘2003-02-01’
target_date = {‘wday’=>date.wday, ‘firstrun’=>firstrun.to_s,
‘first_run_days_ago’=>(date - firstrun).to_i, ‘week’=>date.week,
… }

if task.rules.match?(target_date)
… OK
end

(Although using a block passed to Hash.new should probably be faster
than calculating all possible values every time)

I’ve got code which does something similar, but it’s not specifically
targeted at date matching - drop me a line if it would be of interest.

HTH,

Geoff.

···

On 2004.03.18 06:40, Hal Fulton wrote:

I want to allow recurring tasks. Some of these will be simple, like
“Every Monday.” Others will be more complex, like “Every 2nd and 4th
Friday.” Some might not even be based on weeks or months at all, but
might be like: “Every ten days, no matter what.”

There’d also be an option to give advance warning (N days) on each
event.

So the question becomes: Given a date (typically “today”) and a list
of recurring tasks, how do I determine which ones need to be
displayed?


I’m sorry if I photographed your pet pig, but catholics blow up digital
cameras.

Quoteing hal9000@hypermetrics.com, on Thu, Mar 18, 2004 at 03:40:06PM +0900:

I want to allow recurring tasks. Some of these will be simple, like
“Every Monday.” Others will be more complex, like “Every 2nd and 4th
Friday.” Some might not even be based on weeks or months at all, but
might be like: “Every ten days, no matter what.”

There’d also be an option to give advance warning (N days) on each
event.

So the question becomes: Given a date (typically “today”) and a list
of recurring tasks, how do I determine which ones need to be displayed?

Funny, I’m working on this right now!

I’m adding iCalendar (RFC 2445) support to my vCard library (which is
being renamed vPim).

iCalendar allows events, todos, journal entries, etc. to be stored, and
it also has a very rich language for specifying recurrence rules for
events.

The model is this:

the first occurence it at T

from T you generate new occurences TN for N = 0, … at a fequency of n
(years/months/weeks/days/…)

within that Tn you apply criteria: only on 100th day of year, only on 2
and 3rd sunday of a month, only on tuesdays, …

The criteria results in more or less times.

This might not make sense… here’s some examples:

Weekly on Tuesday and Thursday for 5 weeks:

DTSTART;TZID=US-Eastern:19970902T090000
RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH
or

RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH

==> (1997 9:00 AM EDT)September 2,4,9,11,16,18,23,25,30;October 2

Monthly on the 1st Friday for ten occurrences:

 DTSTART;TZID=US-Eastern:19970905T090000
 RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR

 ==> (1997 9:00 AM EDT)September 5;October 3
     (1997 9:00 AM EST)November 7;Dec 5
     (1998 9:00 AM EST)January 2;February 6;March 6;April 3
     (1998 9:00 AM EDT)May 1;June 5

Every 18 months on the 10th thru 15th of the month for 10
occurrences:

 DTSTART;TZID=US-Eastern:19970910T090000
 RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,
  15

 ==> (1997 9:00 AM EDT)September 10,11,12,13,14,15
     (1999 9:00 AM EST)March 10,11,12,13

From an API point of view, I’ve implemented an Rrule class, that takes
as an argument the DTSTART, and the RRULE.

It has one method, each(), which yields a Time for each occurence of the
event, and it mixes in Enumerable.

So, if you want to know if an event occurs on a particular day, you
can do Rrule#detect, if you want all the events in a period, you can do
Rrule.find_all, etc.

I just finished implementing Rrule#each() Monday night, and reading your
email made me hurry to try and use Enumerable to do these things.

I’ve actually run into an interesting problem, which you ruby gurus can
perhaps help me with:

Rrule#each() could yield forever (“every monday”). But the times
generated by Rrule are ordered, so when I do:

rrule = Rrule.new( … every monday…)

and then try to find all mondays in 2005, I want to break when Rrule
starts yielding times in 2006, but how do I do this? If I just call
“break” I find that Enumerable#find_all is returning nil, but I want
the array (possibly empty) of all occurences!

Similar problem with detect, etc., once the times yielded are out of
range, I’m not interested anymore, and want to break.

What to do?

Anyhow, iCalendar supports TODOs and EVENTs, both of which are something
you sound interested in, with recurrence. I’m building an API supporting
this, and I’d be happy to work with you. If you were using the vPim
library it would give me good impetus to keep adding features to it.
Also, your application would be a great use-case, it would allow me to
direct efforts towards implementing the features immediately useable by
you, rather than wondering what some hypothetical user might want.

Personally, my short-term goals are to implement two tools:

  • a command line utility that when I log in lists all the events/todos
    in the Apple’s iCal that happen in the next week (because I always
    forget to check the calendar)

  • a command line utility that integrates into mutt and allows me to
    respond to iCalendar meeting requests and notifications (because I get
    these at work, and I’ve no way to respond to them)

This code is Beta! I’m working on it as I write, so don’t rip into it
too badly… but I’d like to hear comments. Particularly about how to
use Enumerable with an #each() that generates an infinite, but sorted,
sequence.

Cheers,
Sam

I can’t send any more info… the mail is to big… Here, at least, is
the Rrule docs:

module Vpim

Implements the iCalendar recurence rule syntax. See etc/rrule.txt for the

syntax description and examples from RFC 2445. The description is pretty

hard to understand, but the examples are more helpful.

···

The implementation is pretty complete, but still lacks support for:

TODO - BYWEEKLY, BYWEEKNO, WKST: rules that recur by the week, or are

limited to particular weeks, not hard, but not trivial, I’ll do it for the

next release

TODO - BYHOUR, BYMINUTE, BYSECOND: trivial to do, but I don’t have an

immediate need for them, I’ll do it for the next release

TODO - BYSETPOS: limiting to only certain recurrences in a set (what does

-1, last occurence, mean for an infinitely occuring rule?)

class Rrule
include Enumerable

# The recurrence rule, +rrule+, specifies how to generate a set of times from
# a start time, +dtstart+. It it is nil or empty, the set contains only +dtstart+.
def initialize(dtstart, rrule = nil)
end

# Yields for each +ytime+ in the recurring set of events.
#
# Warning: the set may be infinite! If you want an upper bound on the
# number of occurences, you need to implement it.
#
# TODO - implement some way of providing this upper-bound to the each, but
# the method has to work with the Enumerable mixin, how do I do that?
def each #:yield: ytime
end

# Iterate over all occurences that overlap with the range [t0, t1]. The
# occurence is considered to have a +duration+, and it is in range unless
# it starts at +t1+ or later, or ends before +t0+.
def each_in_range(t0, t1, duration = 0)
  each do |y0|
    y1 = y0 + duration
    break if y0 >= t1
    yield y0 unless y1 < t0
  end
end

end

end

If you read elisp, you can borrow a lot of ideas from the Emacs
“calendar” and “diary” modes. See
http://www.gnu.org/software/emacs/manual/html_chapter/emacs_32.html#SEC412

(or the Emacs info pages on your box.)

On an RPM-based system, you can find the relevant source files by
doing:

$rpm -qa ‘emacs*’
emacs-leim-21.3-7
emacs-el-21.3-7
emacs-21.3-7
$rpm -ql emacs-el-21.3-7 | grep calendar.el
/usr/share/emacs/21.3/lisp/calendar/calendar.el
$grep reminder /usr/share/emacs/21.3/lisp/calendar/*.el | sed ‘s/:/\n /’
/usr/share/emacs/21.3/lisp/calendar/diary-lib.el

diary-rem.sh – repeatedly run the Emacs diary-reminder

/usr/share/emacs/21.3/lisp/calendar/diary-lib.el
Entry is a reminder for diary sexp SEXP. DAYS is either a
/usr/share/emacs/21.3/lisp/calendar/diary-lib.el
reminders beforehand, the diary entry also appears on

···

On Thursday 18 March 2004 01:40, Hal Fulton wrote:

I did a little swapping of ideas with dblack on this. Now I’m
opening discussion to anyone interested.

I’m writing a little to-do manager (chiefly for my own use).

I want to allow recurring tasks.

I did a little swapping of ideas with dblack on this. Now I’m
opening discussion to anyone interested.

I’m writing a little to-do manager (chiefly for my own use).

I want to allow recurring tasks. Some of these will be simple, like
“Every Monday.” Others will be more complex, like “Every 2nd and
4th Friday.” Some might not even be based on weeks or months at
all, but might be like: “Every ten days, no matter what.”

There’d also be an option to give advance warning (N days) on each
event.

So the question becomes: Given a date (typically “today”) and a
list of recurring tasks, how do I determine which ones need to be
displayed?

You might check out the remind application and see what algorithms it
uses. Its been around a while and can do some fairly complex recurring
tasks.

   http://www.roaringpenguin.com/products/remind/index.php 

Also “Calendrical Calculations” by Nachum Dershowitz and Edward
M. Reingold is a great general purpose calendaring book.

enjoy,

-jeremy

···

Jeremy Hinegardner jeremy@hinegardner.org

Hal Fulton said:

I did a little swapping of ideas with dblack on this. Now I’m
opening discussion to anyone interested.

I’m writing a little to-do manager (chiefly for my own use).

I want to allow recurring tasks. Some of these will be simple, like
“Every Monday.” Others will be more complex, like “Every 2nd and 4th
Friday.” Some might not even be based on weeks or months at all, but
might be like: “Every ten days, no matter what.”

There’d also be an option to give advance warning (N days) on each
event.

So the question becomes: Given a date (typically “today”) and a list
of recurring tasks, how do I determine which ones need to be displayed?

What a timely question!

Our local XP users group is addressing the exact same issue. We are
developing a schedule program for reserving rooms at Children’s Hospital.
We need to deal with issues like “Schedule this room every third monday of
the month”.

We decided to use Martin Fowler’s Temporal Expressions pattern. You can
more about that here: http://martinfowler.com/apsupp/recurring.pdf

To summarize, you can create arbitrarily complex temporal expressions by
using a small set of primitives (e.g. YearInRange, DayOfMonth) and a
simple set of combining rules (Union, Intersection, Difference).

For example, you can create simple expressions …

Match the third Monday of the month

days = TExp::WeekDayInMonth.new(3, TExp::MONDAY)

Match any date in June thru August (of any year)

range = TExp::RangeEachYear.by_month(6,9)

And combine them into complex expressions …

Match any 3rd Monday in June through August

dates = TExp::Union.new(days, range)

Test against specific days

dates.includes?(Date.new(2004, 6, 21) # => true
dates.includes?(Date.new(2004, 7, 19) # => true
dates.includes?(Date.new(2004, 6, 14) # => false, not 3rd Monday
dates.includes?(Date.new(2004, 5, 24) # => false, not in Jun-Aug

Does this make sense? Read the PDF for all the gory details.

And you are in luck. There are at least two Temporal Expression libraries
in Ruby. I have one that is part of the ECal project on RubyForge (I
haven’t published any files yet tho … but I can if there is a need).
The other one is Runt (http://runt.rubyforge.org/) by Matthew Lipper,
which is also on RubyForge.

···


– Jim Weirich jim@weirichhouse.org http://onestepback.org

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

Hal Fulton wrote:

I did a little swapping of ideas with dblack on this. Now I’m
opening discussion to anyone interested.

I’m writing a little to-do manager (chiefly for my own use).

I want to allow recurring tasks. Some of these will be simple, like
“Every Monday.” Others will be more complex, like “Every 2nd and 4th
Friday.” Some might not even be based on weeks or months at all, but
might be like: “Every ten days, no matter what.”

There’d also be an option to give advance warning (N days) on each
event.

So the question becomes: Given a date (typically “today”) and a list
of recurring tasks, how do I determine which ones need to be displayed?

What about using regexps?

You can look at this whole problem as making queries against time-points
expressed in several equivalent coordinate systems, such as:

(day of week, ordinal of day in month, month of year)

(day of month, month of year)

(day of week, week of year)

(day of year)

Now, if you don’t mind translating your given data into these coordinate
systems, expressed as a string, then the query can be expressed as a
regex, or a logical combination of regexes.

For example,

require ‘date’

today = Date.today

def ord(x); (x-1)/7+1; end

def coords(date)
[
“day-ordinal-month: %d, %d, %d” % [date.wday, ord(date.mday),
date.mon],
“day-month: %d, %d” % [date.mday, date.mon],
“day-week: %d, %d” % [date.wday, date.cweek],
“day of year: %d” % date.yday,
“Julian day: %d” % date.jd,
“year: %d” % date.year
].join("; ")
end

coords = coords(today)

p coords

every_thurs = /day-week: 4/

puts “It’s Thursday again.” if coords =~ every_thurs

first_or_third_thurs = /day-ordinal-month: 4, (1|3)/

puts “It’s the 1st or 3rd Thursday again.” if coords =~ first_or_third_thurs

Ok, so regexes can’t handle everything:

tenth_day = proc {|d| d[/Julian day: (\d+)/, 1] % 10 ==
Date.new(2004,1,8).jd % 10}

puts “It’s a multiple of ten days since 8 Jan 2004” if tenth_day

Sam Roberts wrote:

Funny, I’m working on this right now!

I’m adding iCalendar (RFC 2445) support to my vCard library (which is
being renamed vPim).

[snip much fascinating stuff]

Sam,

This sounds pretty cool. I was going to write a little thing for my
own use only, but this could turn into more, especially since you
are into the same stuff.

Let’s talk…

Hal

Hal:

I am now only 1,700 messages behind on ruby-talk, but I thought that I would
respond to this – and tell you what the PalmOS platform does for alarms.

Basically, any application is allowed to set a single alarm at a time. The
OS wakes up periodically (~1 minute) to service the alarms; when it detects
that an alarm is due, it hands it over to the application to handle through
its notification manager. The application, after handling the alarm’s
notification, is responsible for setting the next alarm for the
application.

From the perspective of Tycho, I would recommend that you make each alert
responsible for setting its next alert in a master alert list. It might look
like this in the definition of the alert:

Name: New Year’s Day
StartDate: 2001.01.01
Recurs: [object representing this date every year]
NextDate: 2005.01.01

In your alarm file, you might have:

Today: 2004.04.18
2004.05.12: [Mother’s Day (US), …]
2004.05.24: [Memorial Day (US), …]

2005.01.01: [New Year’s Day]

You would remove the appropriate date’s alarm listing, but call
#next_alert_date on each object in the day’s list.

This is provided without having read the rest of the thread you had here :slight_smile:

-austin

···


austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2004.04.18
* 22:17:08

Gerard A.W. Vreeswijk wrote:

···

On Thu, 18 Mar 2004 15:40:06 +0900, Hal Fulton hal9000@hypermetrics.com wrote:

So the question becomes: Given a date (typically “today”) and a list
of recurring tasks, how do I determine which ones need to be displayed?

I don’t get it. Are you asking for something along the lines of Perl’s date-parser in Date::Manip?
If so, then I think you should have a look at the source(s) of the relevant perl functions / methods in Date::Manip.

I don’t see what parsing has to do with this.
I’ll check out these links, though.

Hal

Joel VanderWerf wrote:

Goofed on that last bit:

tenth_day = proc {|d| d[/Julian day: (\d+)/, 1] % 10 ==
Date.new(2004,1,8).jd % 10}

puts “It’s a multiple of ten days since 8 Jan 2004” if tenth_day

tenth_day = proc { |d|
d[/Julian day: (\d+)/, 1].to_i % 10 == Date.new(2004,1,8).jd % 10
}

puts “It’s a multiple of ten days since 8 Jan 2004” if tenth_day[coords]

The complete output is:

“day-ordinal-month: 4, 3, 3; day-month: 18, 3; day-week: 4, 12; day of
year: 78; Julian day: 2453083; year: 2004”
It’s Thursday again.
It’s the 1st or 3rd Thursday again.
It’s a multiple of ten days since 8 Jan 2004

Jeremy Hinegardner wrote:

You might check out the remind application and see what algorithms it
uses. Its been around a while and can do some fairly complex recurring
tasks.

   http://www.roaringpenguin.com/products/remind/index.php 

Coolness, thank you.

Also “Calendrical Calculations” by Nachum Dershowitz and Edward
M. Reingold is a great general purpose calendaring book.

Noted and added to my list. :slight_smile:

Hal

Jim Weirich wrote:

What a timely question!

Our local XP users group is addressing the exact same issue. We are
developing a schedule program for reserving rooms at Children’s Hospital.
We need to deal with issues like “Schedule this room every third monday of
the month”.

We decided to use Martin Fowler’s Temporal Expressions pattern. You can
more about that here: http://martinfowler.com/apsupp/recurring.pdf

I’ve never heard of that one before!

As Mr. Spock would say: “Fascinating, Captain.”

To summarize, you can create arbitrarily complex temporal expressions by
using a small set of primitives (e.g. YearInRange, DayOfMonth) and a
simple set of combining rules (Union, Intersection, Difference).

The need for intersection is obvious. I don’t immediately see the need
for union, however.

For example, you can create simple expressions …

Hmm, should we overload && and || for these? Maybe someone already has.

Does this make sense? Read the PDF for all the gory details.

Reading now.

And you are in luck. There are at least two Temporal Expression libraries
in Ruby. I have one that is part of the ECal project on RubyForge (I
haven’t published any files yet tho … but I can if there is a need).
The other one is Runt (http://runt.rubyforge.org/) by Matthew Lipper,
which is also on RubyForge.

Will also check out runt.

Thanks much,
Hal

Joel VanderWerf wrote:

Joel VanderWerf wrote:

Goofed on that last bit:

tenth_day = proc {|d| d[/Julian day: (\d+)/, 1] % 10 ==
Date.new(2004,1,8).jd % 10}

puts “It’s a multiple of ten days since 8 Jan 2004” if tenth_day

tenth_day = proc { |d|
d[/Julian day: (\d+)/, 1].to_i % 10 == Date.new(2004,1,8).jd % 10
}

puts “It’s a multiple of ten days since 8 Jan 2004” if tenth_day[coords]

The complete output is:

“day-ordinal-month: 4, 3, 3; day-month: 18, 3; day-week: 4, 12; day of
year: 78; Julian day: 2453083; year: 2004”
It’s Thursday again.
It’s the 1st or 3rd Thursday again.
It’s a multiple of ten days since 8 Jan 2004

Joel,

This technique is pretty clever, but it is a different shape

Hal

···

from my brain.

DayOfMonth(1).difference(DayOfWeek(:sunday)).union(
DayOfMonth(2).intersection(DayOfWeek(:monday))
)

I believe that this imaginary code would give you every first of the
month, unless it falls on a sunday, then it gives the second of the
month. Tré chic! (yeah, I probly misspelled that.)

I’d have to agree with Mr. Spock. :slight_smile:

–Mark

···

On Mar 19, 2004, at 1:56 PM, Hal Fulton wrote:

Jim Weirich wrote:

As Mr. Spock would say: “Fascinating, Captain.”

To summarize, you can create arbitrarily complex temporal expressions
by
using a small set of primitives (e.g. YearInRange, DayOfMonth) and a
simple set of combining rules (Union, Intersection, Difference).

The need for intersection is obvious. I don’t immediately see the need
for union, however.

The need for intersection is obvious. I don’t immediately see the need
for union, however.

DayOfMonth(1).difference(DayOfWeek(:sunday)).union(
DayOfMonth(2).intersection(DayOfWeek(:monday))
)

This weighs a ton (reminds me of Java), how about
(DayOfMonth(1) & not(DayOfWeek(:sunday))) | # maybe .not ?
(DayOfMonth(2) & DayOfWeek(:monday) )

or even
(dom(1) & not(dow(:sunday))) | (dom(2) & dow(:monday))

It’s pretty easy to implement with the same technique Criteria uses
(building the AST using Ruby expressions).

Since the temporal expression must be scoped somehow (you want that for
a particular month, or a number of them), a naïve implementation could
even use set operations (using Array); e.g.

(for month == March 2004)
(note: SomeDayObject objects carry complete date info, only the dom is
shown here for briefness)
DayOfMonth(1) => [SomeDayObject(1)]
DayOfWeek(:sunday) => [SomeDayObject(7), SomeDayObject(14),
SomeDayObject(21),SomeDayObject(28)]

DayOfMonth(1) - DayOfWeek(:sunday) => [SomeDayObject(1)]

Intersection is Array#&, union would be Array#+. You’d then just have
to take the first element if you only want one day, or the whole array
if you want a set.

For example, you can create simple expressions …

Hmm, should we overload && and || for these? Maybe someone already has.

&& or || can’t be overloaded; & and | work nicely though.

I believe that this imaginary code would give you every first of the
month, unless it falls on a sunday, then it gives the second of the
month. Tré chic! (yeah, I probly misspelled that.)

I’d expect it to give out the first of the month unless it falls on a
sunday, or the 2nd of the month if it is a monday (else nothing), but
maybe I just have different semantics in mind.

···

On Sat, Mar 20, 2004 at 08:15:40AM +0900, Mark Hubbart wrote:


Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

People disagree with me. I just ignore them.
– Linus Torvalds, regarding the use of C++ for the Linux kernel

The need for intersection is obvious. I don’t immediately see the
need
for union, however.

DayOfMonth(1).difference(DayOfWeek(:sunday)).union(
DayOfMonth(2).intersection(DayOfWeek(:monday))
)

This weighs a ton (reminds me of Java), how about
(DayOfMonth(1) & not(DayOfWeek(:sunday))) | # maybe .not ?
(DayOfMonth(2) & DayOfWeek(:monday) )

I agree :slight_smile: I wouldn’t ever suggest using huge, non-verbal method names
like that in real life. I used them there mainly for clarity; since I
was demonstrating usefulness of union and difference.

perhaps, using possible real syntax:
Tempexp.new{ every(:dom=>1).except(:sunday) &
every(:dom=>1).when(:monday) }

You could make standard methods (every, except, when, and, not…) and
alias them to the appropriate & ^ | operators. This would let someone
present it in the way they feel is clearest.

or even
(dom(1) & not(dow(:sunday))) | (dom(2) & dow(:monday))

It’s pretty easy to implement with the same technique Criteria uses
(building the AST using Ruby expressions).

Since the temporal expression must be scoped somehow (you want that for
a particular month, or a number of them), a naïve implementation could
even use set operations (using Array); e.g.

(for month == March 2004)
(note: SomeDayObject objects carry complete date info, only the dom is
shown here for briefness)
DayOfMonth(1) => [SomeDayObject(1)]
DayOfWeek(:sunday) => [SomeDayObject(7), SomeDayObject(14),
SomeDayObject(21),SomeDayObject(28)]

DayOfMonth(1) - DayOfWeek(:sunday) => [SomeDayObject(1)]

Intersection is Array#&, union would be Array#+. You’d then just have
to take the first element if you only want one day, or the whole array
if you want a set.

Interesting idea… :slight_smile:

For example, you can create simple expressions …

Hmm, should we overload && and || for these? Maybe someone already
has.

&& or || can’t be overloaded; & and | work nicely though.

well, that sucks. I didn’t know that, but I tried it, and you are right!

I believe that this imaginary code would give you every first of the
month, unless it falls on a sunday, then it gives the second of the
month. Tré chic! (yeah, I probly misspelled that.)

I’d expect it to give out the first of the month unless it falls on a
sunday, or the 2nd of the month if it is a monday (else nothing), but
maybe I just have different semantics in mind.

Correct. :slight_smile: I believe that it comes out to the same thing, though. But
yours is more literally correct. It really means first-of-the-month
non-sundays and second-of-the-month mondays (implying that sunday was
first-of-the-month, that month)

Cheers,
–Mark

···

On Mar 20, 2004, at 1:34 AM, Mauricio Fernández wrote:

On Sat, Mar 20, 2004 at 08:15:40AM +0900, Mark Hubbart wrote:

Mauricio Fernández wrote:

This weighs a ton (reminds me of Java), how about
(DayOfMonth(1) & not(DayOfWeek(:sunday))) | # maybe .not ?
(DayOfMonth(2) & DayOfWeek(:monday) )

or even
(dom(1) & not(dow(:sunday))) | (dom(2) & dow(:monday))

Yes, that’s more like what I’m thinking.

It’s pretty easy to implement with the same technique Criteria uses
(building the AST using Ruby expressions).

The Criteria approach makes me a tiny bit nervous. David Black could
tell you why.

[snippage]

Hmm, should we overload && and || for these? Maybe someone already has.

&& or || can’t be overloaded; & and | work nicely though.

Yes, that’s what I meant. :wink:

Hal