銀月の符号

Python 使い見習いの日記・雑記

カレンダー出力

ちょっと HTML でカレンダーつくる必要に迫られたのだけれども、手打ちすると td タグの海の中に誤りが混入しそうな気がしたので(弱気すぎるだろ、オレ)。ここは半自動生成したいなと思った。なにか作っておけば、もし翌月の分を作る時、楽ができるという副作用もあるはず。

ただし、すぐ作れるかどうかは重要。手打ちすればすぐなのだから、1日仕事になってしまうならばダメだ。理想は HTML 手打ちしたのと変わらない速度で HTML 吐くスクリプトが出来上がること。

とりあえず、 0 から作るのは避けてなんかさがす。いや、思い出す。

Python にはカレンダー関連を面倒見てくれる標準ライブラリ calendar モジュールがある。コード読解に 15 分。だいたいつかんだので、 Calendar クラスを希望の形に拡張してみる。こんなかんじか?

コード
# coding: utf-8

import calendar

class SpamCalendar(calendar.Calendar):
    weekday = (u'月', u'火', u'水', u'木', u'金', u'土', u'日')

    def __init__(self, event, firstweekday=0):
        self.event = event
        super(SpamCalendar, self).__init__(firstweekday)

    def formatweekday(self, theday):
        return u'<td>%s</td>' % self.weekday[theday]

    def formatweekheader(self):
        s = u''.join(self.formatweekday(i) for i in self.iterweekdays())
        return u'<tr>%s</tr>' % s

    def formatday(self, theday, theweekday):
        if theday == 0:
            return u'<td>&nbsp;</td>'
        else:
            return '<td>%d</td>' % theday

    def formatweek(self, theweek):
        s = u''.join(self.formatday(d, wd) for d, wd in theweek)
        return u'<tr>%s</tr>' % s

    def formateventday(self, theday, theweekday):
        if theday == 0:
            return u'<td>&nbsp;</td>'
        else:
            event = u'<br />'.join(self.event.get(theday, [u'&nbsp;']))
            return u'<td>%s</td>' % event
        
    def formatweekevent(self, week):
        s = u''.join(self.formateventday(d, wd) for d, wd in week)
        return u'<tr>%s</tr>' % s

    def formatmonth(self, year, month):
        v = []
        a = v.append
        a(u'<table class="month">')
        a(u'\n')
        a(self.formatweekheader())
        a(u'\n')
        for week in self.monthdays2calendar(year, month):
            a(self.formatweek(week))
            a(self.formatweekevent(week))
            a(u'\n')
        a(u'</table>')
        return u''.join(v)

def main():
    event_one = u'1のつく日'
    event_first = u'月始め'
    event = {
            1: [event_one, event_first],
            11: [event_one],
            21: [event_one],
            }

    cal = SpamCalendar(event, 6)

    print cal.formatmonth(2009, 11)

if __name__ == '__main__':
    main()

20分弱…完成! 実行してみる。

出力

1234567
1のつく日
月始め
      
891011121314
   1のつく日   
15161718192021
      1のつく日
22232425262728
       
2930     
       

内容はあっている。が、希望のものとは少し違う。イベント列は2列欲しい。もうすこし手直しがいるみたい。できあがったら CSS で飾ればいける、かな。とりあえず table.month td に width と height の指定は要りそう。