i got tired of all calendars being based on this system, which i find too restricted. You cannot encode many types of holiday, Easter or many religious festivals. You can't track the moon either.

So, i wrote my own calendar and the events work as a start datetime, end datetime and optional recurrence. the recurrence is encoded as a small script "generator" statement (LUA actually). The script uses a rich set of built in "function kit" such as "fullmoon", "newmoon" etc. Things like Easter need the moon, and so do many religious festivals.

The generator returns the NEXT datetime, so that the calendar can chain these events so that scrolling works nicely and you only calculate what you need.

# Example Generators

## Annual date (eg birthdays)
return nextMonthDay(d, september, 23)

## monthly date, `v' of month
return nextMonth(d)+v-1

## first monday of next month
return nthkDay(nextMonth(d),1,monday)

## last sunday in a month next year
eg last sunday next march. finds next april 1 and works back to last sunday
return nthkDay(nextMonthDay(d+7,april,1),-1,sunday)

next monday on or after next May 1st
eg may day
return nthkDay(nextMonthDay(d,may,1),1,monday)

eg last monday in may, spring bank holiday
return nthkDay(nextMonthDay(d+7,june,1),-1,monday)

## third tuesday of the month
return nthkDay(nextMonth(d),3,tuesday)

## fourth thursday in November (Thanksgiving USA)
return nthkDay(nextMonthDay(d, november, 1), 4, thursday)

## second monday of october (Thanksgiving Canada)
return nthkDay(nextMonthDay(d, october, 1), 2, monday)

## mothers day UK
4th sunday of Lent = 3 weeks before easter sunday
return cal.nextEasterSunday(d+22)-21

## first and third thursday of the month
function cvrs(da)
local d1 = nthkDay(thisMonth(da),3,thursday)
local d2 = nthkDay(nextMonth(da),1,thursday)
if d1 > da then return d1 end
return d2
end
return cvrs(d)

## first weekday of month
function weekday(d)
local da = cal.dayOfWeek(d)
if da == sunday then d = d + 1
elseif da == saturday then d = d + 2 end
return d
end
return weekday(nextMonth(d))

## first weekday on or before given date of of month
function weekdayb4(d)
local da = cal.dayOfWeek(d)
if da == sunday then d = d - 2
elseif da == saturday then d = d - 1 end
return d
end
return weekdayb4(nextMonth(d)+25) -- example

## easter
return cal.nextEasterSunday(d)

eg good friday
return cal.nextEasterSunday(d+3)-2

eg. Easter monday
return cal.nextEasterSunday(d)+1

eg shrove tuesday
return cal.nextEasterSunday(d+48)-47

##* moon phases
return cal.nextNewMoon(d+1)
return cal.nextFullMoon(d+1)

## ramadan

this generates the logical 1/9 islamic. Howeever the fast of Ramadan
is often started the day earlier if based on the observational moon
note that it always begins the sunset beforehand.

y,m = cal.islamicFromFixed(d)
if m >= 9 then y=y+1 end
return cal.fixedFromIslamic(y,9,1)

-- end of ramadan is the end of month 9
-- Eid-al-Fitr (Ramadan ends)
y,m = cal.islamicFromFixed(d+1)
if m >= 10 then y=y+1 end
return cal.fixedFromIslamic(y,9,30)

-- Al-Hijra (Islamic New Year)
return cal.fixedFromIslamic(cal.islamicFromFixed(d)+1,1,1)

## Hebrew

-- Rosh Hashanah (Jewish New Year)
-- 1st of Tishri
return cal.fixedFromHebrew(cal.hebrewFromFixed(d)+1,7,1)

-- Yom Yippur
-- tishri 10
return cal.fixedFromHebrew(cal.hebrewFromFixed(d)+1,7,10)