The year 2006 approaches, with a new set of statutory holidays.
The idea of having to annually dig up the list of holidays for next year,
and encode them statically into a file filled me with disgust. So I saved
myself 5 minutes of work by spending 30 minutes piecing together some remind
rules for calculating Canadian Statutory holidays.
These rules are derived (copied, mostly) from the examples/defs.rem distributed with remind.
SET Sunday 0 SET Monday 1 SET Tuesday 2 SET Wednesday 3 SET Thursday 4 SET Friday 5 SET Saturday 6 SET Sun 0 SET Mon 1 SET Tue 2 SET Wed 3 SET Thu 4 SET Fri 5 SET Sat 6 SET Jan 1 SET Feb 2 SET Mar 3 SET Apr 4 SET May 5 SET Jun 6 SET Jul 7 SET Aug 8 SET Sep 9 SET Oct 10 SET Nov 11 SET Dec 12 SET January 1 SET February 2 SET March 3 SET April 4 SET May 5 SET June 6 SET July 7 SET August 8 SET September 9 SET October 10 SET November 11 SET December 12 SET Week_1 1 SET Week_2 8 SET Week_3 15 SET Week_4 22 FSET _last(mo) "1 " + MON((mo%12)+1)+" --7" FSET _trig() TRIGGER(TRIGDATE()) FSET _trig2(days) TRIGGER(TRIGDATE()+days) FSET _back(days) TRIGGER(TODAY()-days)
# Federal Holidays
# http://www.pch.gc.ca/progs/cpsc-ccsp/jfa-ha/index_e.cfm
SET easter EASTERDATE(YEAR(TODAY()))
REM [TRIGGER(easter-46)] MSG Ash Wednesday
REM [TRIGGER(easter-7)] MSG Palm Sunday
OMIT [TRIGGER(easter-2)] MSG Good Friday
OMIT [TRIGGER(easter)] MSG Easter Sunday
OMIT [TRIGGER(easter+1)] MSG Easter Monday
REM [TRIGGER(easter+39)] MSG Ascension Day
REM [TRIGGER(easter+49)] MSG Pentecost
OMIT Jan 1 MSG New Year's Day
REM Feb 15 MSG National Flag of Canada Day
REM Mar 17 MSG St. Patrick's Day
REM Mon May 18 SCANFROM [_back(7)] SATISFY 1
OMIT [_trig()] MSG Victoria Day
# Victoria Day is the Monday Preceeding May 25
REM Jul 1 SCANFROM [_back(7)] SATISFY 1
IF WKDAYNUM(TRIGDATE()) == Sun
OMIT [_trig2(+1)] MSG Canada day
ELSE
OMIT [_trig()] MSG Canada day
ENDIF
# Jul 2 is Canada DAY when Jul 1 is a Sunday
REM Mon Sep [Week_1] SCANFROM [_back(7)] SATISFY 1
OMIT [_trig()] MSG Labor Day
REM Nov 11 MSG Remembrance Day
REM Mon Oct [Week_2] SCANFROM [_back(7)] SATISFY 1
OMIT [_trig()] MSG Thanksgiving Day
OMIT Dec 25 MSG Christmas Day
OMIT Dec 26 MSG Boxing Day
# Provincial Holidays
# http://www.pch.gc.ca/progs/cpsc-ccsp/jfa-ha/prov_e.cfm
REM Mon Feb [Week_3] MSG Family Day (Alberta)
#REM Jun 24 MSG National Day (Quebec)
REM Mon Aug [Week_1] MSG BC Day
REM Mon Aug [Week_1] MSG New Brunswick Day
REM Mon Aug [Week_1] MSG Heritage Day (Alberta)
REM Mon Aug [Week_1] MSG Civic Holida (Manitoba,Saskatchewan,Ontario)
# Misc
REM Feb 2 MSG Ground Hog Day
REM Feb 14 MSG Valentine's Day
REM Apr 1 MSG April Fool's Day
REM Sun May [Week_2] MSG Mother's Day
REM Sun Jun [Week_3] MSG Father's Day
REM Oct 31 MSG Halloween
# Daylight Savings time.
REM Sun Apr 1 MSG Daylight Savings Time starts (loose an hour)
REM Sun [_last(Oct)] MSG Daylight Savings Time over (gain an hour)
jack |
posted Mon Dec 12 18:41:41 2005 |
#
category:
projects/calendar
The remind program doesn't actually put reminders into the calendars it
renders (for good reason), o had to use part of the MSG. I could have used a
SPECIAL ALARM, but then I only get the date.
For example, I use this:
REM Monday 2005 AT 09:00 DURATION 1:00 MSG +90 Weekly meeting
To provide a 90 minute reminder of the event. This meant that my parsing of the output of remind had to get smarter. So I rewrote it in perl:
<font color="#8080ff"><b>#!/usr/bin/env perl</b></font>
<font color="#ffff00"><b>use warnings</b></font>;
<font color="#ffff00"><b>use strict</b></font>;
<font color="#ffff00"><b>use </b></font>Date::Parse;
<font color="#ffff00"><b>my</b></font> <font color="#00ffff"><b>$CALENDAR</b></font>=<font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>./calendar</b></font><font color="#ff40ff"><b>"</b></font>;
<font color="#ffff00"><b>my</b></font> <font color="#00ffff"><b>$WEEKS</b></font>=<font color="#ff40ff"><b>2</b></font>;
<font color="#ffff00"><b>print</b></font> <font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>BEGIN:VCALENDAR</b></font><font color="#ff6060"><b>\n</b></font><font color="#ff40ff"><b>"</b></font>;
<font color="#ffff00"><b>print</b></font> <font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>VERSION:1.0</b></font><font color="#ff6060"><b>\n</b></font><font color="#ff40ff"><b>"</b></font>;
<font color="#ffff00"><b>open</b></font>(<font color="#00ffff"><b>CAL</b></font>, <font color="#ff40ff"><b>'</b></font><font color="#ff40ff"><b>-|</b></font><font color="#ff40ff"><b>'</b></font>, <font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>remind -s+${WEEKS} -b1 ${CALENDAR}</b></font><font color="#ff40ff"><b>"</b></font>)
<font color="#ffff00"><b>or</b></font> <font color="#ffff00"><b>die</b></font> <font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>can't start remind: </b></font><font color="#00ffff"><b>$!</b></font><font color="#ff40ff"><b>"</b></font>;
<font color="#ffff00"><b>while</b></font> (<font color="#00ffff"><b><CAL></b></font>) {
<font color="#ffff00"><b>chomp</b></font>;
<font color="#ffff00"><b>my</b></font> (<font color="#00ffff"><b>$date</b></font>, <font color="#00ffff"><b>$x</b></font>, <font color="#00ffff"><b>$y</b></font>, <font color="#00ffff"><b>$duration</b></font>, <font color="#00ffff"><b>$z</b></font>, <font color="#00ffff"><b>$msg</b></font>)
= <font color="#ffff00"><b>split</b></font>( <font color="#ffff00"><b>/</b></font><font color="#ff6060"><b>\s</b></font><font color="#ffff00"><b>/</b></font>, <font color="#00ffff"><b>$_</b></font>, <font color="#ff40ff"><b>6</b></font>);
<font color="#ffff00"><b>my</b></font> <font color="#00ffff"><b>@msg</b></font>=<font color="#ffff00"><b>split</b></font>(<font color="#ffff00"><b>/</b></font><font color="#ff6060"><b>\s</b></font><font color="#ffff00"><b>/</b></font>, <font color="#00ffff"><b>$msg</b></font>);
<font color="#ffff00"><b>my</b></font> (<font color="#00ffff"><b>$start</b></font>,<font color="#00ffff"><b>$alarm</b></font>);
<font color="#ffff00"><b>next</b></font> <font color="#ffff00"><b>if</b></font> ( <font color="#00ffff"><b>$date</b></font> =~ <font color="#ffff00"><b>m/</b></font><font color="#ff40ff"><b>^#</b></font><font color="#ffff00"><b>/</b></font> ) ;
<font color="#ffff00"><b>if</b></font> ( <font color="#00ffff"><b>$duration</b></font> <font color="#ffff00"><b>eq</b></font> <font color="#ff40ff"><b>'</b></font><font color="#ff40ff"><b>*</b></font><font color="#ff40ff"><b>'</b></font> ) { <font color="#00ffff"><b>$duration</b></font>=<font color="#ff40ff"><b>60</b></font> ; }
<font color="#ffff00"><b>if</b></font> ( <font color="#00ffff"><b>$z</b></font> <font color="#ffff00"><b>eq</b></font> <font color="#ff40ff"><b>'</b></font><font color="#ff40ff"><b>*</b></font><font color="#ff40ff"><b>'</b></font> ) {
<font color="#00ffff"><b>$start</b></font>=<font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>00:00</b></font><font color="#ff40ff"><b>"</b></font>;
<font color="#00ffff"><b>$duration</b></font>=<font color="#ff40ff"><b>1439</b></font>;
<font color="#00ffff"><b>$alarm</b></font>=<font color="#ff40ff"><b>0</b></font>;
} <font color="#ffff00"><b>else</b></font> {
<font color="#00ffff"><b>$start</b></font>=<font color="#ffff00"><b>shift</b></font> <font color="#00ffff"><b>@msg</b></font>;
<font color="#ffff00"><b>if</b></font> ( <font color="#00ffff"><b>$msg</b></font>[<font color="#ff40ff"><b>0</b></font>] =~<font color="#ffff00"><b> /</b></font><font color="#ff6060"><b>\+</b></font><font color="#ff6060"><b>.*</b></font><font color="#ffff00"><b>/</b></font> ) {
<font color="#00ffff"><b>$alarm</b></font> = <font color="#ffff00"><b>shift</b></font> <font color="#00ffff"><b>@msg</b></font>;
<font color="#00ffff"><b>$alarm</b></font> =~ <font color="#ffff00"><b>s/</b></font><font color="#ff6060"><b>\+</b></font><font color="#ffff00"><b>//</b></font>
} <font color="#ffff00"><b>else</b></font> {
<font color="#00ffff"><b>$alarm</b></font> = <font color="#ff40ff"><b>0</b></font>
}
<font color="#00ffff"><b>$msg</b></font>=<font color="#ffff00"><b>join</b></font>(<font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b> </b></font><font color="#ff40ff"><b>"</b></font>,<font color="#00ffff"><b>@msg</b></font>);
}
<font color="#ffff00"><b>my</b></font> <font color="#00ffff"><b>$t</b></font>=str2time(<font color="#ff40ff"><b>"</b></font><font color="#00ffff"><b>$date</b></font><font color="#ff40ff"><b> </b></font><font color="#00ffff"><b>$start</b></font><font color="#ff40ff"><b>"</b></font>);
<font color="#ffff00"><b>die</b></font> <font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>unable to parse </b></font><font color="#00ffff"><b>$date</b></font><font color="#ff40ff"><b> </b></font><font color="#00ffff"><b>$start</b></font><font color="#ff40ff"><b>"</b></font> <font color="#ffff00"><b>if</b></font> <font color="#ffff00"><b>not</b></font> <font color="#ffff00"><b>defined</b></font> <font color="#00ffff"><b>$t</b></font>;
<font color="#ffff00"><b>my</b></font> <font color="#00ffff"><b>$d</b></font>=<font color="#00ffff"><b>$duration</b></font>*<font color="#ff40ff"><b>60</b></font>;
<font color="#ffff00"><b>my</b></font> <font color="#00ffff"><b>$a</b></font>=<font color="#00ffff"><b>$alarm</b></font>*<font color="#ff40ff"><b>60</b></font>;
<font color="#ffff00"><b>my</b></font> (<font color="#00ffff"><b>$sec</b></font>,<font color="#00ffff"><b>$min</b></font>,<font color="#00ffff"><b>$hour</b></font>,<font color="#00ffff"><b>$mday</b></font>,<font color="#00ffff"><b>$mon</b></font>,<font color="#00ffff"><b>$year</b></font>) = <font color="#ffff00"><b>localtime</b></font>(<font color="#00ffff"><b>$t</b></font>);
<font color="#00ffff"><b>$year</b></font> += <font color="#ff40ff"><b>1900</b></font>; <font color="#00ffff"><b>$mon</b></font>++;
<font color="#ffff00"><b>my</b></font> <font color="#00ffff"><b>$s_string</b></font>=<font color="#ffff00"><b>sprintf</b></font>(<font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>%04d%02d%02dT%02d%02d%02d</b></font><font color="#ff40ff"><b>"</b></font>,
<font color="#00ffff"><b>$year</b></font>, <font color="#00ffff"><b>$mon</b></font>, <font color="#00ffff"><b>$mday</b></font>, <font color="#00ffff"><b>$hour</b></font>, <font color="#00ffff"><b>$min</b></font>, <font color="#00ffff"><b>$sec</b></font>);
(<font color="#00ffff"><b>$sec</b></font>,<font color="#00ffff"><b>$min</b></font>,<font color="#00ffff"><b>$hour</b></font>,<font color="#00ffff"><b>$mday</b></font>,<font color="#00ffff"><b>$mon</b></font>,<font color="#00ffff"><b>$year</b></font>) = <font color="#ffff00"><b>localtime</b></font>(<font color="#00ffff"><b>$t</b></font>+<font color="#00ffff"><b>$d</b></font>);
<font color="#00ffff"><b>$year</b></font> += <font color="#ff40ff"><b>1900</b></font>; <font color="#00ffff"><b>$mon</b></font>++;
<font color="#ffff00"><b>my</b></font> <font color="#00ffff"><b>$e_string</b></font>=<font color="#ffff00"><b>sprintf</b></font>(<font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>%04d%02d%02dT%02d%02d%02d</b></font><font color="#ff40ff"><b>"</b></font>,
<font color="#00ffff"><b>$year</b></font>, <font color="#00ffff"><b>$mon</b></font>, <font color="#00ffff"><b>$mday</b></font>, <font color="#00ffff"><b>$hour</b></font>, <font color="#00ffff"><b>$min</b></font>, <font color="#00ffff"><b>$sec</b></font>);
(<font color="#00ffff"><b>$sec</b></font>,<font color="#00ffff"><b>$min</b></font>,<font color="#00ffff"><b>$hour</b></font>,<font color="#00ffff"><b>$mday</b></font>,<font color="#00ffff"><b>$mon</b></font>,<font color="#00ffff"><b>$year</b></font>) = <font color="#ffff00"><b>localtime</b></font>(<font color="#00ffff"><b>$t</b></font>-<font color="#00ffff"><b>$a</b></font>);
<font color="#00ffff"><b>$year</b></font> += <font color="#ff40ff"><b>1900</b></font>; <font color="#00ffff"><b>$mon</b></font>++;
<font color="#ffff00"><b>my</b></font> <font color="#00ffff"><b>$a_string</b></font>=<font color="#ffff00"><b>sprintf</b></font>(<font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>%04d%02d%02dT%02d%02d%02d</b></font><font color="#ff40ff"><b>"</b></font>,
<font color="#00ffff"><b>$year</b></font>, <font color="#00ffff"><b>$mon</b></font>, <font color="#00ffff"><b>$mday</b></font>, <font color="#00ffff"><b>$hour</b></font>, <font color="#00ffff"><b>$min</b></font>, <font color="#00ffff"><b>$sec</b></font>);
<font color="#ffff00"><b>print</b></font> <font color="#ff40ff"><b>"</b></font><font color="#ff6060"><b>\n</b></font><font color="#ff40ff"><b>"</b></font>;
<font color="#ffff00"><b>print</b></font> <font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>BEGIN:VEVENT</b></font><font color="#ff6060"><b>\n</b></font><font color="#ff40ff"><b>"</b></font>;
<font color="#ffff00"><b>print</b></font> <font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>CATEGORIES:MEETING</b></font><font color="#ff6060"><b>\n</b></font><font color="#ff40ff"><b>"</b></font>;
<font color="#ffff00"><b>print</b></font> <font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>SUMMARY:</b></font><font color="#00ffff"><b>$msg</b></font><font color="#ff6060"><b>\n</b></font><font color="#ff40ff"><b>"</b></font>;
<font color="#ffff00"><b>print</b></font> <font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>DTSTART:</b></font><font color="#00ffff"><b>$s_string</b></font><font color="#ff6060"><b>\n</b></font><font color="#ff40ff"><b>"</b></font>;
<font color="#ffff00"><b>print</b></font> <font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>DTEND:</b></font><font color="#00ffff"><b>$e_string</b></font><font color="#ff6060"><b>\n</b></font><font color="#ff40ff"><b>"</b></font>;
<font color="#ffff00"><b>print</b></font> <font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>AALARM:</b></font><font color="#00ffff"><b>$a_string</b></font><font color="#ff6060"><b>\n</b></font><font color="#ff40ff"><b>"</b></font> <font color="#ffff00"><b>if</b></font> ( <font color="#00ffff"><b>$alarm</b></font> != <font color="#ff40ff"><b>0</b></font> ) ;
<font color="#ffff00"><b>print</b></font> <font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>END:VEVENT</b></font><font color="#ff6060"><b>\n</b></font><font color="#ff40ff"><b>"</b></font>;
<font color="#ffff00"><b>print</b></font> <font color="#ff40ff"><b>"</b></font><font color="#ff6060"><b>\n</b></font><font color="#ff40ff"><b>"</b></font>;
}
<font color="#ffff00"><b>print</b></font> <font color="#ff40ff"><b>"</b></font><font color="#ff6060"><b>\n</b></font><font color="#ff40ff"><b>"</b></font>;
<font color="#ffff00"><b>print</b></font> <font color="#ff40ff"><b>"</b></font><font color="#ff40ff"><b>END:VCALENDAR</b></font><font color="#ff6060"><b>\n</b></font><font color="#ff40ff"><b>"</b></font>;
jack |
posted Thu Sep 22 23:09:16 2005 |
#
category:
projects/calendar
Tam and I crossed the line sometime this spring where we could no longer keep all of our commitments in our heads. Equally likely for this is the increasing scope and complexity of our commitments, or our decreasing mental facilities.
First off, we decided to use remind. It seems to have the most descriptive frontend for making entries, and many flexible backends for displaying calendars as postscript, html, or even vcard. We created a different calender file for different categories, and a top level calendar that includes them all.
Since remind uses flat text files, we decided to use monotone. This allows us to do all of the usual good version control stuff (ancestry, logs, branching and merging), as well has having a distributed repository (useful for moving files around).
A simple script, run from cron, maintains an online, HTML version of the calendar:
#!/bin/sh
PATH=/bin:/usr/bin:/opt/bin
IN=....
OUT=....
cd $IN && monotone update
year=$(date +%Y)
for i in $(seq 12)
do
month=$(date +%B --date=$i/01/$year)
remind -p calendar $month 1 $year | rem2html > $OUT/${i}.html
done
I have a cell phone that has a calendar application, and a bluetooth interface, so I thought I'd give having the calendars on my phone a swing.
This turns out to be easy possible using gammu. Gammu can interpret and load vcs (I'm sure there's actually a standard for that) onto my Sony Ericsson z600 over bluetooth. This requires getting bluetooth running under linux, and getting gammu working over bluetooth.
Bluetooth has been around a while now, so there is a fair amount of documentation on how to get it working. It basically boils down to:
Once you've established that the phone and the linux box can talk (using l2ping), then setting up gammu is pretty easy. Here is my gammurc:
[gammu] port = BT ADDRESS connection = blueat logformat = textall startinfo = yes
You can test out if gammu is working by simply doing a gammu --identify. After doing a bit of reverse engineering, and some digging around on the web about how to load calendars onto phones using gammu, I patched together a script to create the vcs files from remind.
#!/bin/bash
# XXX: any event without a duration gets assigned a duration of 60 minutes
# XXX: any all-day event gets assigned a start time of midnight and a duration
# of 23:59
# XXX: this most certainly does not deal with events that wrap over day
# boundaries correctly.
CAL=./calendar
WEEKS=2
remind -s+${WEEKS} -b2 ${CAL} | \
( echo BEGIN:VCALENDAR
echo VERSION:1.0
while read date x y duration start msg
do
[ $start = '*' ] && { start=0000 ; duration=1439 ; }
[ $duration = '*' ] && duration=60
date=${date//\//}
sh=$(printf %.2d $((start/60)))
sm=$(printf %.2d $((start%60)))
eh=$(printf %.2d $(( (start+duration) / 60 )))
em=$(printf %.2d $(( (start+duration) % 60 )))
# the printfs are needed for zero padding
echo
echo BEGIN:VEVENT
echo CATEGORIES:MEETING
echo SUMMARY:$msg
echo DTSTART:${date}T${sh}${sm}00
echo DTEND:${date}T${eh}${em}00
echo END:VEVENT
echo
done
echo END:VCALENDAR
)
Since bluetooth has suprising range, I also wrote a script to be run from cron that will backup the phone, and then load the calendars onto the phone once a day. I usually have my phone somewhere around me, and I'm usually sleeping or at least at home at 0400h, so that's when the cron jobs run.
#!/bin/sh
PATH=/opt/bin
IN=...
date=$(date +%y%m%d)
BACKUP=/usr/home/jack/var/phone/z600.backup.${date}
tmp=/usr/home/jack/tmp/sync.${$}.vcs
# gammu uses filename extensions to recognize files.
gammu --backup $BACKUP -yes
cd $IN && monotone sync && monotone update
./scripts/make_vcs.sh > $tmp
echo ALL | gammu --restore $tmp
rm $tmp
Only 2 weeks of calendar are loaded onto the phone, mainly to make for reasonable transfer times.
There are still a few things left to do. Having the phone issue reminders would be cool. As would having email reminders.
jack |
posted Wed Jul 20 11:56:50 2005 |
#
category:
projects/calendar
A weblog by Jack Cummings