試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

Pythonのカレンダーモジュールを用いる

Pythonにはカレンダーに関する処理を行うのに便利なcalendarというモジュールがある。

(2015/1/25)以前は2ページに分けて公開していたが、このページに統合した。リファレンスのリンク先も修正し、サンプルコードについてもPython 3で動作することなどを目的に一部修正した。

  1. カレンダーとしての内部の処理を利用するのに適したcalndar.Calendarクラス
    1. 開始曜日の変更
  2. 整形表示を容易に行うためのクラス
  3. calendar.Calendarクラスを用いた例
  4. calendar.TextCalendarクラスを用いた例
  5. calendar.HTMLCalendarクラスを用いた例

カレンダーとしての内部の処理を利用するのに適したcalndar.Calendarクラス

calndar.Calendarクラスのオブジェクトを作成し、メンバ関数itermonthdates()に西暦年と月を指定するとジェネレータオブジェクト(関連記事)が得られる。

>>> import calendar
>>> c = calendar.Calendar ()
>>> g = c.itermonthdates (2010, 6)
>>> g
<generator object itermonthdates at 0xXXXXXXXXXXXX>

「次に進める」処理を繰り返しながら得られるオブジェクト(datetime.dateオブジェクト)は指定した前の月の最後の週の最初の曜日(既定では月曜)の日から始まり、指定された月の全ての日が得られた後、次の月の最初の週の最後の曜日(既定では日曜)の日で終わる。この形により、どの日が何曜日なのかが分かるようになっている。

(前の月の最後の月曜(最初の曜日))
>>> g.next ()
datetime.date (2010, 5, 31)

(以下同様に続いていく)
>>> g.next ()
datetime.date (2010, 6, 1)
>>> g.next ()
datetime.date (2010, 6, 2)
...
>>> g.next ()
datetime.date (2010, 6, 30)
>>> g.next ()
datetime.date (2010, 7, 1)
>>> g.next ()
datetime.date (2010, 7, 2)
>>> g.next ()
datetime.date (2010, 7, 3)

(次の月の最初の日曜(最後の曜日))
>>> g.next ()
datetime.date (2010, 7, 4)
(最後に到達してStopIteration例外が発生)
>>> g.next ()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

実際としてはenumerate()を同時に用いてループをする形が便利。

for i, d in enumerate (c.itermonthdates ([西暦年], [月])):
  # ここに表示などの処理を記述
  # 「i % 7」の値によってその日の曜日が分かる
  # d.monthの値がitermonthdates()に指定したものと一致すればその月
  # 異なれば前の月の最後か次の月の最初のどちらか

開始曜日の変更
calndar.Calendarクラスと後述の派生クラスでは週の開始の曜日を月曜としているが、コンストラクタ引数により変更することができる(月曜が0,日曜が6)。

(日曜を週の始まりとする)
c = calendar.Calendar (6)

整形表示を容易に行うためのクラス

バージョン2.5以上のPythonではcalコマンドのような整形済みテキストを出力するcalndar.TextCalendarクラスとHTMLソース(テーブルを使用)を出力するcalndar.HTMLCalendarクラスが用意されている。いずれもメンバ関数formatmonth()に西暦年と月を指定することで文字列オブジェクトとして結果を得る形となる。

calendar.Calendarクラスを用いた例

この例では、指定された年/月の日付と曜日を順に出力し、前後の月の日は「---」付きで出力する。開始の曜日を日曜としたものと月曜としたものの2つのバージョンを続けて出力する(以下の例について全て同様)。
[任意]ファイル名: calendartest.py

#! /usr/bin/python
# -*- coding: utf-8 -*-

# 指定された年/月の日付と対応する曜日を出力

from __future__ import print_function

import calendar
import sys

if len (sys.argv) != 3:
  sys.exit ('USAGE: {0} [year] [month]'.format (sys.argv[0]))

year = month = None
try:
  year = int (sys.argv[1])
except ValueError:  # 数値でない文字列の場合
  sys.exit ('ERROR: invalid year ({0})'.format (sys.argv[1]))
try:
  month = int (sys.argv[2])
except ValueError:
  sys.exit ('ERROR: invalid month ({0})'.format (sys.argv[2]))

# もし不正な月が指定されると後でValueErrorが発生する
# 今回はここで値をチェックしておくことにする
if month > 12 or month < 1:
  sys.exit ('ERROR: invalid month ({0})'.format (month))

# 内部的に日曜からのカレンダーで処理する場合
c = calendar.Calendar (6)  # 6を指定すると日曜から開始
wday = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
for i, d in enumerate (c.itermonthdates (year, month)):
  if d.month == month:
    print ('{0}/{1} {2}'.format (d.month, d.day, wday[i % 7]))
  else:
    print ('--- {0}/{1} {2} ---'.format (d.month, d.day, wday[i % 7]))

print ('-' * 80)

# 内部的に月曜からのカレンダーで処理する場合
c = calendar.Calendar ()  # 開始曜日の既定値は0(月曜)
wday = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
for i, d in enumerate (c.itermonthdates (year, month)):
  if d.month == month:
    print ('{0}/{1} {2}'.format (d.month, d.day, wday[i % 7]))
  else:
    print ('--- {0}/{1} {2} ---'.format (d.month, d.day, wday[i % 7]))

下は実行例。

$ [calendartest.pyの場所] 2010 6
--- 5/30 Sun ---
--- 5/31 Mon ---
6/1 Tue
6/2 Wed
6/3 Thu
6/4 Fri
6/5 Sat
6/6 Sun
6/7 Mon
6/8 Tue
6/9 Wed
6/10 Thu
6/11 Fri
6/12 Sat
6/13 Sun
6/14 Mon
6/15 Tue
6/16 Wed
6/17 Thu
6/18 Fri
6/19 Sat
6/20 Sun
6/21 Mon
6/22 Tue
6/23 Wed
6/24 Thu
6/25 Fri
6/26 Sat
6/27 Sun
6/28 Mon
6/29 Tue
6/30 Wed
--- 7/1 Thu ---
--- 7/2 Fri ---
--- 7/3 Sat ---
--------------------------------------------------------------------------------
--- 5/31 Mon ---
6/1 Tue
6/2 Wed
6/3 Thu
6/4 Fri
6/5 Sat
6/6 Sun
6/7 Mon
6/8 Tue
6/9 Wed
6/10 Thu
6/11 Fri
6/12 Sat
6/13 Sun
6/14 Mon
6/15 Tue
6/16 Wed
6/17 Thu
6/18 Fri
6/19 Sat
6/20 Sun
6/21 Mon
6/22 Tue
6/23 Wed
6/24 Thu
6/25 Fri
6/26 Sat
6/27 Sun
6/28 Mon
6/29 Tue
6/30 Wed
--- 7/1 Thu ---
--- 7/2 Fri ---
--- 7/3 Sat ---
--- 7/4 Sun ---

calendar.TextCalendarクラスを用いた例

この例では、指定された年/月の整形済みカレンダー(テキスト形式)を出力する。
[任意]ファイル名: textcalendartest.py

#! /usr/bin/python
# -*- coding: utf-8 -*-

# 指定された年/月の整形済みカレンダーを出力(calコマンドに近い形)

from __future__ import print_function

import calendar
import sys

# 下のコメントを解除してロケールを設定すると表記が日本語になるが
# 表示に問題がある場合がある
#import locale; locale.setlocale (locale.LC_ALL, '')

if len (sys.argv) != 3:
  sys.exit ('USAGE: {0} [year] [month]'.format (sys.argv[0]))

year = month = None
try:
  year = int (sys.argv[1])
except ValueError:  # 数値でない文字列の場合
  sys.exit ('ERROR: invalid year ({0})'.format (sys.argv[1]))
try:
  month = int (sys.argv[2])
except ValueError:
  sys.exit ('ERROR: invalid month ({0})'.format (sys.argv[2]))

# もし不正な月が指定されると後でValueErrorが発生する
# 今回はここで値をチェックしておくことにする
if month > 12 or month < 1:
  sys.exit ('ERROR: invalid month ({0})'.format (month))

# 内部的に日曜からのカレンダーで処理する場合
tc = calendar.TextCalendar (6)  # 「6」を指定すると日曜から開始
print (tc.formatmonth (year, month))
#print (tc.formatyear (year))     # 年全体

print ('-' * 80)

# 内部的に月曜からのカレンダーで処理する場合
tc = calendar.TextCalendar ()  # 開始曜日の既定値は0(月曜)
print (tc.formatmonth (year, month))
#print (tc.formatyear (year))

下は実行例。

$ [textcalendartest.pyの場所] 2010 6
     June 2010
Su Mo Tu We Th Fr Sa
       1  2  3  4  5
 6  7  8  9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30

--------------------------------------------------------------------------------
     June 2010
Mo Tu We Th Fr Sa Su
    1  2  3  4  5  6
 7  8  9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30

calendar.HTMLCalendarクラスを用いた例

この例では、指定された年/月の整形済みHTMLカレンダーのソースを出力する。
[任意]ファイル名: htmlcalendartest.py

#! /usr/bin/python
# -*- coding: utf-8 -*-

# 指定された年/月の整形済みHTMLカレンダーを出力

from __future__ import print_function

import calendar
import sys

# 下のコメントを解除してロケールを設定すると表記が日本語になる
#import locale; locale.setlocale (locale.LC_ALL, '')

if len (sys.argv) != 3:
  sys.exit ('USAGE: {0} [year] [month]'.format (sys.argv[0]))

year = month = None
try:
  year = int (sys.argv[1])
except ValueError:  # 数値でない文字列の場合
  sys.exit ('ERROR: invalid year ({0})'.format (sys.argv[1]))
try:
  month = int (sys.argv[2])
except ValueError:
  sys.exit ('ERROR: invalid month ({0})'.format (sys.argv[2]))

# もし不正な月が指定されると後でValueErrorが発生する
# 今回はここで値をチェックしておくことにする
if month > 12 or month < 1:
  sys.exit ('ERROR: invalid month ({0})'.format (month))

# 内部的に日曜からのカレンダーで処理する場合
hc = calendar.HTMLCalendar (6)  # 「6」を指定すると日曜から開始
print (hc.formatmonth (year, month))
#print (hc.formatyear (year))     # 年全体

print ('-' * 80)

# 内部的に月曜からのカレンダーで処理する場合
hc = calendar.HTMLCalendar ()  # 開始曜日の既定値は0(月曜)
print (hc.formatmonth (year, month))
#print (hc.formatyear (year))

2010年6月の実行例は下のようになる。
(1つ目/日曜日から土曜日)

June 2010
SunMonTueWedThuFriSat
  12345
6789101112
13141516171819
20212223242526
27282930   

(2つ目/月曜日から日曜日)

June 2010
MonTueWedThuFriSatSun
 123456
78910111213
14151617181920
21222324252627
282930    

参考URL:

使用したバージョン: