静かなる名辞

pythonとプログラミングのこと

【python】calendarモジュールの使い方

 calendarモジュールは標準ライブラリに入っていて、曜日や日付の計算にはけっこう便利なモジュールらしいです。

 でもあまり周知されていないので、使い方を(自分用に)メモっておきます。

 ドキュメントはここです。
8.2. calendar — 一般的なカレンダーに関する関数群 — Python 3.6.5 ドキュメント

 目次

introduction

>>> import calendar
>>> print(calendar.month(2018, 4))
     April 2018
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.setfirstweekday(calendar.SUNDAY)
>>> print(calendar.month(2018, 4))
     April 2018
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

 オブジェクト指向でも関数型でもない、手続き型パラダイムって感じです>setfirstweekday()。ちょっとおっかない。このへんに依存する処理をしたいときは、import先やimport元で変なことにならないよう、一々確認してあげる必要があるということであります。

 まあ、ちゃんとクラスインスタンスを作れるので、そっちを使うようにすれば良いのですが。

calendarモジュールのクラス

 以下のクラスがあります。

  • Calendar
  • TextCalendar
  • HTMLCalendar
  • LocaleTextCalendar
  • LocaleHTMLCalendar

 なんとなく酷い気もしますが、大目に見ましょう。HTMLはどうでも良いので(いや、使いたいって人もいるだろうけど)、TextCalendarでintroductionと同じことをやってみます。

>>> from calendar import TextCalendar
>>> tcalendar = TextCalendar(firstweekday=6)
>>> print(tcalendar.formatmonth(2018, 4))
     April 2018
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

 firstweekday=6は、0が月曜日で6が日曜日という仕様なので、こうしております。

 ロケールも一応やってみます。

>>> from calendar import LocaleTextCalendar
>>> ltc = LocaleTextCalendar(firstweekday=6, locale="ja_JP.UTF-8")
>>> print(ltc.formatmonth(2018, 4, w=3))
     April 2018
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

>>> ltc = LocaleTextCalendar(firstweekday=6, locale="ja_JP.UTF-8")
>>> print(ltc.formatmonth(2018, 4, w=5))
          42018
 日   月   火   水   木   金   土
  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

 フォントの問題があるので、何をやってもあまり綺麗には見えません。半角英数と全角文字を同じ文字幅で表示するフォントがあれば、綺麗に見えることでしょう。

機能

 さて、存在するクラスのうち、

  • TextCalendar
  • HTMLCalendar
  • LocaleTextCalendar
  • LocaleHTMLCalendar

 これらはCalendarのサブクラスです。出力形式をformatするだけに存在しています。

 曜日や日付の計算に使いたいと思う機能は、

  • Calendar

 クラスに集約されています。

 そしてここには大したメソッド数はありません。なので、一つずつ紹介していきましょう。

iterweekdays()

曜日の数字を一週間分生成するイテレータを返します。イテレータから得られる最初の数字は firstweekday が返す数字と同じになります。

>>> from calendar import Calendar
>>> cl = Calendar()
>>> cl.iterweekdays()
<generator object Calendar.iterweekdays at 0x7fca5a3d3ca8>
>>> list(cl.iterweekdays())
[0, 1, 2, 3, 4, 5, 6]

 これだけ。

itermonthdates(year, month)

year 年 month (1–12) 月に対するイテレータを返します。 このイテレータはその月の全ての日、およびその月が始まる前の日とその月が終わった後の日のうち、週の欠けを埋めるために必要な日を (datetime.date オブジェクトとして) 返します。

 指定した年月の日を返しますが、週の中途半端なところから始まったり、中途半端なところで終わったりすると、前後の月も週が中途半端じゃなくなる範囲まで出してくれます。長いので整形した出力を見せます。

>>> cl = Calendar(firstweekday=6)
>>> list(cl.itermonthdates(2018, 4))
[datetime.date(2018, 4, 1), datetime.date(2018, 4, 2), datetime.date(2018, 4, 3), 
datetime.date(2018, 4, 4), datetime.date(2018, 4, 5), datetime.date(2018, 4, 6),
datetime.date(2018, 4, 7), datetime.date(2018, 4, 8), datetime.date(2018, 4, 9), 
datetime.date(2018, 4, 10), datetime.date(2018, 4, 11), datetime.date(2018, 4, 12), 
datetime.date(2018, 4, 13), datetime.date(2018, 4, 14), datetime.date(2018, 4, 15), 
datetime.date(2018, 4, 16), datetime.date(2018, 4, 17), datetime.date(2018, 4, 18), 
datetime.date(2018, 4, 19), datetime.date(2018, 4, 20), datetime.date(2018, 4, 21), 
datetime.date(2018, 4, 22), datetime.date(2018, 4, 23), datetime.date(2018, 4, 24), 
datetime.date(2018, 4, 25), datetime.date(2018, 4, 26), datetime.date(2018, 4, 27), 
datetime.date(2018, 4, 28), datetime.date(2018, 4, 29), datetime.date(2018, 4, 30), 
datetime.date(2018, 5, 1), datetime.date(2018, 5, 2), datetime.date(2018, 5, 3), 
datetime.date(2018, 5, 4), datetime.date(2018, 5, 5)]

 あんまり嬉しくないかも・・・。

itermonthdays2(year, month)

year 年 month 月に対する itermonthdates() と同じようなイテレータを返します。生成されるのは日付の数字と曜日を表す数字のタプルです。

 上とほぼ同じ。返り値の型だけ違います。

>>> list(cl.itermonthdays2(2018, 4))
[(1, 6), (2, 0), (3, 1), (4, 2), (5, 3), (6, 4), (7, 5), (8, 6), (9, 0), (10, 1), 
(11, 2), (12, 3), (13, 4), (14, 5), (15, 6), (16, 0), (17, 1), (18, 2), (19, 3), (20, 4), 
(21, 5), (22, 6), (23, 0), (24, 1), (25, 2), (26, 3), (27, 4), (28, 5), (29, 6), (30, 0), 
(0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]

 前後月の日付は0で返されるようです。タプルの二番目の要素は例の曜日を表す数字。

 これを処理すると簡単そうで良いですね。

itermonthdays(year, month)

year 年 month 月に対する itermonthdates() と同じようなイテレータを返します。生成されるのは日付の数字だけです。

 なんで同じようなメソッドがいくつもあるんだろうか。

>>> list(cl.itermonthdays(2018, 4))
[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, 0, 0, 0, 0, 0]

 (命名が)投げやりだなー(棒)。

monthdatescalendar(year, month)

year 年 month 月の週のリストを返します。週は全て七つの datetime.date オブジェクトからなるリストです。

 週のリストを返すんだって。メソッド名からは想像できない機能で、ちょっとびっくりしています。

>>> list(cl.monthdatescalendar(2018, 4))
[[datetime.date(2018, 4, 1), datetime.date(2018, 4, 2), datetime.date(2018, 4, 3), datetime.date(2018, 4, 4), datetime.date(2018, 4, 5), datetime.date(2018, 4, 6), datetime.date(2018, 4, 7)],
[datetime.date(2018, 4, 8), datetime.date(2018, 4, 9), datetime.date(2018, 4, 10), datetime.date(2018, 4, 11), datetime.date(2018, 4, 12), datetime.date(2018, 4, 13), datetime.date(2018, 4, 14)],
[datetime.date(2018, 4, 15), datetime.date(2018, 4, 16), datetime.date(2018, 4, 17), datetime.date(2018, 4, 18), datetime.date(2018, 4, 19), datetime.date(2018, 4, 20), datetime.date(2018, 4, 21)],
[datetime.date(2018, 4, 22), datetime.date(2018, 4, 23), datetime.date(2018, 4, 24), datetime.date(2018, 4, 25), datetime.date(2018, 4, 26), datetime.date(2018, 4, 27), datetime.date(2018, 4, 28)],
[datetime.date(2018, 4, 29), datetime.date(2018, 4, 30), datetime.date(2018, 5, 1), datetime.date(2018, 5, 2), datetime.date(2018, 5, 3), datetime.date(2018, 5, 4), datetime.date(2018, 5, 5)]]

monthdays2calendar(year, month)

year 年 month 月の週のリストを返します。週は全て七つの日付の数字と曜日を表す数字のタプルからなるリストです。

>>> list(cl.monthdays2calendar(2018, 4))
[[(1, 6), (2, 0), (3, 1), (4, 2), (5, 3), (6, 4), (7, 5)], 
[(8, 6), (9, 0), (10, 1), (11, 2), (12, 3), (13, 4), (14, 5)], 
[(15, 6), (16, 0), (17, 1), (18, 2), (19, 3), (20, 4), (21, 5)], 
[(22, 6), (23, 0), (24, 1), (25, 2), (26, 3), (27, 4), (28, 5)], 
[(29, 6), (30, 0), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5)]]

 ・・・説明、要る?

monthdayscalendar(year, month)

year 年 month 月の週のリストを返します。週は全て七つの日付の数字からなるリストです。

>>> list(cl.monthdayscalendar(2018, 4))
[[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, 0, 0, 0, 0, 0]]

 このモジュールの世界観は理解してしまえばとてもわかりやすいので、そういう意味では良いと思います。

yeardatescalendar(year, width=3)

指定された年のデータを整形に向く形で返します。返される値は月の並びのリストです。月の並びは最大で width ヶ月(デフォルトは3ヶ月)分です。各月は4ないし6週からなり、各週は1ないし7日からなります。各日は datetime.date オブジェクトです。

 以下のメソッドの出力は長いので省略。どんなものが返るのかは、実行すればすぐ理解できます。

yeardays2calendar(year, width=3)

指定された年のデータを整形に向く形で返します (yeardatescalendar() と同様です)。週のリストの中が日付の数字と曜日の数字のタプルになります。月の範囲外の部分の日付はゼロです。

yeardayscalendar(year, width=3)

指定された年のデータを整形に向く形で返します (yeardatescalendar() と同様です)。週のリストの中が日付の数字になります。月の範囲外の日付はゼロです。

 あー、疲れた。モジュールのリファレンスを書き写すような真似してもあまり意味なかったですね。

便利なusage

 そのうち(思いついたら)書きます。第n何曜日の計算とかに使えると思います。

まとめ

 ちょっと残念だけど、たまに使えるシチュエーションがありそうではある。