Calendar script

From ConTeXt wiki

I decided not to spoil the first day of the new year by doing real work. I also wanted to get to know a bit more about lua...

The background: we produce a calendar with pictures of our kids every year. Of course, I wanted to do that with ConTeXt. There is a number of precooked scripts to produce various calendars for LaTeX, but nothing for ConTeXt, as far as I could see. So I decided to write something myself. I first did it in perl, with the Date::Calc module. Then, I tried to achieve the same result with a lua script. Without the convenience of a precooked date module, I had to do all the calculations of holidays and days of the week myself, taking my cues mostly from wikipedia, but it was relatively easy.

You invoke the script from the command line: lua calendar.lua XXXX, and it will produce a file kalenderXXXX.tex, where XXXX is a number between 1900 and 2299. This file can then be typeset with ConTeXt. The result will be a calendar for the year XXXX, with every month on one page; Sundays and German official holidays are in boldface; the first day of the week is Monday. The names of the months are in German, but that should be easy to modify. We have a simple version in black and white which we print on colored paper and into which real pictures are glued, and a digital version which we send to friends via e-mail where the pages of the typeset pdf have colored backgrounds and the pictures are included.

Neither the lua code nor the TeX code is particularly beautiful (hey, this was my first day with lua), but it may be interesting for someone out there.

#!/usr/local/bin/lua

year = arg[1]

-- create boolean switch for leap years

if (math.mod(year,4) == 0) then 
   if (math.mod(year,100) == 0) then 
      if (math.mod(year,400) == 0) then 
	 leap_year = true
	 else leap_year = false end
      else leap_year = true end
   else leap_year = false
end

-- calculate easter sunday, according to Meeus/Jones/Butcher
-- (see http://en.wikipedia.org/wiki/Computus#Meeus.2FJones.2FButcher_Gregorian_algorithm )

a = math.mod(year,19)

b = math.floor(year / 100)

c = math.mod(year,100)

d = math.floor(b / 4)

e = math.mod(b,4)

f = math.floor((b + 8) / 25) 

g = math.floor((b - f + 1) / 3)

h = math.mod((19 * a + b - d - g + 15),30)

i = math.floor(c / 4)

k = math.mod(c,4)

L = math.mod((32 + 2 * e + 2 * i - h - k),7)

m = math.floor((a + 11 * h + 22 * L) / 451)

eastersundaymonth = math.floor((h + L - 7 * m + 114) / 31)

eastersunday = math.mod((h + L - 7 * m + 114),31) + 1

-- now, calculate other Christian holidays which depend on Easter Sunday

if eastersunday + 1 <= 31 then 
eastermonday,eastermondaymonth = (eastersunday + 1),eastersundaymonth 
else eastermonday,eastermondaymonth = (eastersunday - 30),(eastersundaymonth + 1) 
end

if eastersunday -2 >= 1 then 
goodfriday,goodfridaymonth = (eastersunday - 2),eastersundaymonth 
else goodfriday,goodfridaymonth = (eastersunday + 29),(eastersundaymonth - 1) 
end

if leap_year == true then 
   if eastersundaymonth == 4 then juliansunday = 91 + eastersunday
   elseif eastersundaymonth == 3 then juliansunday = 60 + eastersunday end
else if eastersundaymonth == 4 then juliansunday = 90 + eastersunday
   elseif eastersundaymonth == 3 then juliansunday = 59 + eastersunday end
end 

if leap_year == true then
if juliansunday + 50 <= 152 then pentecostmonday,pentecostmonth = (juliansunday - 71),5
elseif (juliansunday + 50) > 152 then pentecostmonday,pentecostmonth = (juliansunday - 102),6
end
if juliansunday + 60 <= 152 then corpusday,corpusmonth = (juliansunday - 61),5
elseif (juliansunday + 60) > 152 then corpusday,corpusmonth = (juliansunday - 92),6
end
if juliansunday + 39 <= 121 then ascensionday,ascensionmonth = (juliansunday - 52),4
elseif (juliansunday + 39) > 121 then ascensionday,ascensionmonth = (juliansunday - 82),5
end
else
if juliansunday + 50 <= 151 then pentecostmonday,pentecostmonth = (juliansunday - 70),5
elseif (juliansunday + 50) > 151 then pentecostmonday,pentecostmonth = (juliansunday - 101),6
end

if juliansunday + 60 <= 151 then corpusday,corpusmonth = (juliansunday - 60),5
elseif (juliansunday + 60) > 151 then corpusday,corpusmonth = (juliansunday - 91),6
end

if juliansunday + 39 <= 120 then ascensionday,ascensionmonth = (juliansunday - 51),4
elseif (juliansunday + 39) > 120 then ascensionday,ascensionmonth = (juliansunday - 81),5
end
end

-- we open our outfile

outfile = io.open("kalender"..year..".tex","w")

outfile:write("%%% calendar for the year "..year..", produced by the lua script\n%%% calendar.lua on "..os.date().."\n\n")

-- and first write the header of our document

outfile:write("\\enableregime[utf]\n\n\\setuppagenumbering[state=stop]\n\n\\setuplayout[footer=0cm,\n\t margin=0cm,\n\t backspace=1cm,\n\t 
width=fit]\n\n\\setupcolors[state=start]\n\n\\definecolor [mycyan] [r=0, g=0.8, b=1]\n\n\\starttext\n\n%\\showframe\n\n% \\setupbackgrounds[page]
[background=color,backgroundcolor=darkred]\n\n% \\midaligned{\\externalfigure[title][width=.9\\textwidth]}\n\n\\switchtobodyfont[38pt]\n\n
\\startalignment[middle]\n\n\\strut\n\n\\vfill\n\n"..year.."\n\n\\blank[2cm]\n\nKalender\n\n\\blank[2cm]\n\n\\strut\n\n\\stopalignment\n\n
\\page\n\n\\switchtobodyfont[20pt]\n\n\\strut\n\n\\vfill\n\n% \\midaligned{\\externalfigure[january][width=.9\\textwidth]}\n\n
% \\setupbackgrounds[page] [background=color,backgroundcolor=orange]\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc Januar}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")

-- calculate day of week for 1/1, according to Babwani's formula
-- (see http://www.geocities.com/sohaelbabwani/algorithm.html )

myyear = math.abs(year)

if myyear < 2000 then n,y = 19,(myyear - 1900)
else n,y = 20,(myyear - 2000)
end

if leap_year == true then
janfirst = math.mod((math.floor((5 * y) / 4) + 6 + 1 - (2 * (math.mod(n,4)))),7)
else
janfirst = math.mod((math.floor((5 * y) / 4) + 0 + 1 - (2 * (math.mod(n,4)))),7)
end

if janfirst == 0 then janfirst = 7 end
if janfirst == 1 then janfirst = 8 end

janindent = janfirst - 1

for jan = 1,janindent do outfile:write("\\NC ") end

if janfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("{\\bf 1} ") end 

for day = 2,31 do
   if leap_year == true then
   weekday = math.mod((math.floor((5 * y) / 4) + 6 + day - (2 * (math.mod(n,4)))),7)
   else
   weekday = math.mod((math.floor((5 * y) / 4) + 0 + day - (2 * (math.mod(n,4)))),7)
   end
   if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
   elseif day == 6 then outfile:write("\\NC {\\bf "..day.."} ")
   else  outfile:write("\\NC "..day.." ") end
end

outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[february][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n
\\midaligned{\\sc Februar}\n\n\\placetable[bottom][none]{none}{\n
\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")

if leap_year == true then
febult,febvar = 29,2
else febult,febvar = 28,3
end

febfirst = math.mod((math.floor((5 * y) / 4) + febvar + 1 - (2 * (math.mod(n,4)))),7)

if febfirst == 0 then febfirst = 7 end
if febfirst == 1 then febfirst = 8 end

febindent = febfirst - 1

for feb = 1,febindent do outfile:write("\\NC ") end

if febfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end 

for day = 2,febult do
   weekday = math.mod((math.floor((5 * y) / 4) + febvar + day - (2 * (math.mod(n,4)))),7)
   if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
   else  outfile:write("\\NC "..day.." ") end
end

outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[march][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc März}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")

marfirst = math.mod((math.floor((5 * y) / 4) + 3 + 1 - (2 * (math.mod(n,4)))),7)

if marfirst == 0 then marfirst = 7 end
if marfirst == 1 then marfirst = 8 end

marindent = marfirst - 1

for mar = 1,marindent do outfile:write("\\NC ") end

if marfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end

for day = 2,31 do
   weekday = math.mod((math.floor((5 * y) / 4) + 3 + day - (2 * (math.mod(n,4)))),7)
   if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
   elseif goodfridaymonth == 3 and day == goodfriday then outfile:write("\\NC {\\bf "..day.."} ")
   elseif eastermondaymonth == 3 and day == eastermonday then outfile:write("\\NC {\\bf "..day.."} ")
   else  outfile:write("\\NC "..day.." ") end
end

outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[april][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc April}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")

aprfirst = math.mod((math.floor((5 * y) / 4) + 6 + 1 - (2 * (math.mod(n,4)))),7)

if aprfirst == 0 then aprfirst = 7 end
if aprfirst == 1 then aprfirst = 8 end

aprindent = aprfirst - 1

for apr = 1,aprindent do outfile:write("\\NC ") end

if aprfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end

for day = 2,30 do
   weekday = math.mod((math.floor((5 * y) / 4) + 6 + day - (2 * (math.mod(n,4)))),7)
   if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
   elseif goodfridaymonth == 4 and day == goodfriday then outfile:write("\\NC {\\bf "..day.."} ")
   elseif eastermondaymonth == 4 and day == eastermonday then outfile:write("\\NC {\\bf "..day.."} ")
   elseif ascensionmonth == 4 and day == ascensionday then outfile:write("\\NC {\\bf "..day.."} ")
   else  outfile:write("\\NC "..day.." ") end
end

outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[may][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc Mai}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")

mayfirst = math.mod((math.floor((5 * y) / 4) + 1 + 1 - (2 * (math.mod(n,4)))),7)

if mayfirst == 0 then mayfirst = 7 end
if mayfirst == 1 then mayfirst = 8 end

mayindent = mayfirst - 1

for may = 1,mayindent do outfile:write("\\NC ") end

if mayfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("{\\bf 1} ") end

for day = 2,31 do
   weekday = math.mod((math.floor((5 * y) / 4) + 1 + day - (2 * (math.mod(n,4)))),7)
   if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
   elseif ascensionmonth == 5 and day == ascensionday then outfile:write("\\NC {\\bf "..day.."} ")
   elseif corpusmonth == 5 and day == corpusday then outfile:write("\\NC {\\bf "..day.."} ")
   elseif pentecostmonth == 5 and day == pentecostmonday then outfile:write("\\NC {\\bf "..day.."} ")
   else  outfile:write("\\NC "..day.." ") end
end

outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[june][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc Juni}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")

junfirst = math.mod((math.floor((5 * y) / 4) + 4 + 1 - (2 * (math.mod(n,4)))),7)

if junfirst == 0 then junfirst = 7 end
if junfirst == 1 then junfirst = 8 end

junindent = junfirst - 1

for jun = 1,junindent do outfile:write("\\NC ") end

if junfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end

for day = 2,30 do
   weekday = math.mod((math.floor((5 * y) / 4) + 4 + day - (2 * (math.mod(n,4)))),7)
   if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n")
   elseif ascensionmonth == 6 and day == ascensionday then outfile:write("\\NC {\\bf "..day.."} ")
   elseif corpusmonth == 6 and day == corpusday then outfile:write("\\NC {\\bf "..day.."} ")
   elseif pentecostmonth == 6 and day == pentecostday then outfile:write("\\NC {\\bf "..day.."} ")
   else  outfile:write("\\NC "..day.." ") end
end

outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[july][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc Juli}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")

julfirst = math.mod((math.floor((5 * y) / 4) + 6 + 1 - (2 * (math.mod(n,4)))),7)

if julfirst == 0 then julfirst = 7 end
if julfirst == 1 then julfirst = 8 end

julindent = julfirst - 1

for jul = 1,julindent do outfile:write("\\NC ") end

if julfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end

for day = 2,31 do
   weekday = math.mod((math.floor((5 * y) / 4) + 6 + day - (2 * (math.mod(n,4)))),7)
   if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n") 
   else  outfile:write("\\NC "..day.." ") end
end

outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[august][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc August}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")

augfirst = math.mod((math.floor((5 * y) / 4) + 2 + 1 - (2 * (math.mod(n,4)))),7)

if augfirst == 0 then augfirst = 7 end
if augfirst == 1 then augfirst = 8 end

augindent = augfirst - 1

for aug = 1,augindent do outfile:write("\\NC ") end

if augfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end

for day = 2,31 do
   weekday = math.mod((math.floor((5 * y) / 4) + 2 + day - (2 * (math.mod(n,4)))),7)
   if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n") 
   else  outfile:write("\\NC "..day.." ") end
end

outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[september][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc September}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")

sepfirst = math.mod((math.floor((5 * y) / 4) + 5 + 1 - (2 * (math.mod(n,4)))),7)

if sepfirst == 0 then sepfirst = 7 end
if sepfirst == 1 then sepfirst = 8 end

sepindent = sepfirst - 1

for sep = 1,sepindent do outfile:write("\\NC ") end

if sepfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end

for day = 2,30 do
   weekday = math.mod((math.floor((5 * y) / 4) + 5 + day - (2 * (math.mod(n,4)))),7)
   if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n") 
   else  outfile:write("\\NC "..day.." ") end
end

outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[october][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc Oktober}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")

octfirst = math.mod((math.floor((5 * y) / 4) + 0 + 1 - (2 * (math.mod(n,4)))),7)

if octfirst == 0 then octfirst = 7 end
if octfirst == 1 then octfirst = 8 end

octindent = octfirst - 1

for oct = 1,octindent do outfile:write("\\NC ") end

if octfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end

for day = 2,31 do
   weekday = math.mod((math.floor((5 * y) / 4) + 0 + day - (2 * (math.mod(n,4)))),7)
   if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n") 
   elseif day == 3 then outfile:write("\\NC {\\bf "..day.."} ")
   else  outfile:write("\\NC "..day.." ") end
end

outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[november][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc November}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")

novfirst = math.mod((math.floor((5 * y) / 4) + 3 + 1 - (2 * (math.mod(n,4)))),7)

if novfirst == 0 then novfirst = 7 end
if novfirst == 1 then novfirst = 8 end

novindent = novfirst - 1

for nov = 1,novindent do outfile:write("\\NC ") end

if novfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("{\\bf 1} ") end

for day = 2,30 do
   weekday = math.mod((math.floor((5 * y) / 4) + 3 + day - (2 * (math.mod(n,4)))),7)
   if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n") 
   else  outfile:write("\\NC "..day.." ") end
end

outfile:write("\n\\stoptabulate\n}\n\n\\page\n\n% \\setupbackgrounds[page] [background=color,backgroundcolor=darkred]\n\n
% \\midaligned{\\externalfigure[december][width=.66\\textwidth]}\n\n\\strut\n\n\\vfill\n\n\\midaligned{\\sc Dezember}\n\n
\\placetable[bottom][none]{none}{\n\\starttabulate[|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|rw(4mm)|]\n")

decfirst = math.mod((math.floor((5 * y) / 4) + 5 + 1 - (2 * (math.mod(n,4)))),7)

if decfirst == 0 then decfirst = 7 end
if decfirst == 1 then decfirst = 8 end

decindent = decfirst - 1

for dec = 1,decindent do outfile:write("\\NC ") end

if decfirst == 8 then outfile:write("{\\bf 1} \\NC \\NR\n")
else outfile:write("1 ") end

for day = 2,31 do
   weekday = math.mod((math.floor((5 * y) / 4) + 5 + day - (2 * (math.mod(n,4)))),7)
   if weekday == 1 then outfile:write("\\NC {\\bf "..day.."} \\NC \\NR\n") 
   elseif day == 25 then outfile:write("\\NC {\\bf "..day.."} ")
   elseif day == 26 then outfile:write("\\NC {\\bf "..day.."} ")
   else  outfile:write("\\NC "..day.." ") end
end

outfile:write("\n\\stoptabulate\n}\n\n\\stoptext\n")

--Thomas 21:56, 1 January 2007 (CET)