

The deceptively complex world of RRULEs - systemizer
https://nylas.com/blog/rrules?hackernews

======
daurnimator
This is actually what prompted me to start work on luatz:
[https://github.com/daurnimator/luatz](https://github.com/daurnimator/luatz)

Trying to implement RRULEs you need all sorts of fun date math + converting
back and forth between time zones.

I never released my ical library (didn't get it open sourced from work); but
luatz lets you write your own :)

~~~
grinich
that's pretty awesome :) too bad about the ics library! the world needs more
tools like this

------
steaminghacker
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)

~~~
grinich
Did you release the code to this? It's a really interesting idea! How did you
ensure they are deterministic or run in constant time? (Or are they not
guaranteed to?)

One of our goals with the Nylas calendar APIs was to make them backwards
compatible, so that developers could still interact with Gcal/Exchange. This
means we're really restricted in what we can expose at this point. Having more
sophisticated tools for defining repeating events or moments in time is
certainly where we want to go, though!

~~~
steaminghacker
I developed this as an app for Windows and Android. it could work on linux
too. Although i didn't release it. The main reason is that i didn't consider
it finished. I did a month view and a day view, but i wanted also the week
view. Although i do use it as my daily calendar.

Complete calendar data and rules are stored as a single text file! that's what
i wanted too (about 1MB for 20 years' data). I can make manual backups!

Regarding the exiting formats, i wrote an importer that converts iCal to LUA.
Basically, just a set of templates. Indeed the original idea was to offer a UI
to people whereby clicking on things would automatically generate bits of LUA
generators - sort of non-programmatic interface for non-power users.

For efficiency (deterministic run-time), each generator takes a date and
returns a date. So that it always returns a later date than it is given.

The calendar is generated on demand, so today I'm viewing August 2015. If i
scroll down to September/October etc. The generators are called until they
return dates _later_ than my current view (or stop generating). I can scroll
down years and years and everything works fine.

Primitives such as "nextNewMoon" etc can either be coded in LUA as part of
initialisation, or added to the internal LUA run-time. The calendar algorithms
are all from "Calendrical Calculations" by Nachum-Dershowitz. Although i
converted them to C++ (from lisp!).

At some point i need to finished the Chinese calendar :-)

~~~
grinich
Software is never finished, you just decide it's ready to ship. :) If you'd
like a smaller audience, I'd love to try it out and maybe write some of my own
patterns/generators. I'm going to check out that book too.

