5.4. calendar — 日期操作

ose: 日历模块是实现用于处理日期的类,以管理面向年/月/周的值。

calendar 模块定义了 Calendar 类, 它封装了值的计算, 例如给定月份或年份中周的日期。此外, TextCalendar 和 HTMLCalendar 类可以生成预格式化的输出。

格式化示例

该 prmonth() 方法是一个简单的函数,可以生成一个月的格式化文本输出。

calendar_textcalendar.py

import calendar

c = calendar.TextCalendar(calendar.SUNDAY)
c.prmonth(2017, 7)

根据美国惯例,该示例配置 TextCalendar 以周日开始周。默认是欧洲惯例,使用星期一开始一周的。

输出如下:

$ python3 calendar_textcalendar.py

     July 2017
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 31

可以使用 HTMLCalendar 和 formatmonth() 生成类似 HTML 的表。 呈现的输出看起来与纯文本版本大致相同,但是用 HTML 标记包装。每个表格单元格都有一个与星期几相对应的类属性,所以这种 HTML 可以通过 CSS 设置样式。

要以不同于其中一个可用默认格式生成输出,请使用 calendar 计算日期格式并将值组织为周和月的范围,然后迭代结果。Calendar 中的 weekheader()monthcalendar(), 和 yeardays2calendar() 方法对此特别有用。

调用 yeardays2calendar() 会生成一系列 “月份行” 列表。每个列表包括月份作为另一个周的列表。这几周是由日期编号(1-31)和工作日编号(0-6)组成的元组列表。超出月份的天数为0。

calendar_yeardays2calendar.py

import calendar
import pprint

cal = calendar.Calendar(calendar.SUNDAY)

cal_data = cal.yeardays2calendar(2017, 3)
print('len(cal_data)      :', len(cal_data))

top_months = cal_data[0]
print('len(top_months)    :', len(top_months))

first_month = top_months[0]
print('len(first_month)   :', len(first_month))

print('first_month:')
pprint.pprint(first_month, width=65)

调用 yeardays2calendar(2017, 3) retu返回2017年的数据,每行三个月。

$ python3 calendar_yeardays2calendar.py

len(cal_data)      : 4
len(top_months)    : 3
len(first_month)   : 5
first_month:
[[(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), (31, 1), (0, 2), (0, 3), (0, 4), (0, 5)]]

这等效于使用 formatyear()获得的数据。

calendar_formatyear.py

import calendar

cal = calendar.TextCalendar(calendar.SUNDAY)
print(cal.formatyear(2017, 2, 1, 1, 3))

对于相同的参数, formatyear() 生成如下输出:

$ python3 calendar_formatyear.py

                              2017

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

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

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

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

 day_nameday_abbrmonth_name, 和 month_abbr 模块用于生产自定义格式化的输出(即,包括HTML输出链路)是有用的。它们会针对当前区域设置自动正确配置。

地区

要为当前默认设置之外的地区生成格式化的日历,我们可以使用 LocaleTextCalendar 或 LocaleHTMLCalendar

calendar_locale.py

import calendar

c = calendar.LocaleTextCalendar(locale='en_US')
c.prmonth(2017, 7)

print()

c = calendar.LocaleTextCalendar(locale='fr_FR')
c.prmonth(2017, 7)

一周的第一天并不是地区设置的一部分,而且数值是从参数获取到 calendar 类,就像常规的 TextCalendar 一样。

$ python3 calendar_locale.py

     July 2017
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
31

    juillet 2017
Lu Ma Me Je Ve Sa Di
                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
31

计算日期

虽然日历模块主要侧重于以各种格式打印完整日历,但它还提供了以其他方式处理日期的有用功能,例如计算重复事件的日期。例如,Python 亚特兰大用户组在每个月的第二个星期四开会。要计算一年会议的日期,请使用 monthcalendar() 的返回值。

calendar_monthcalendar.py

import calendar
import pprint

pprint.pprint(calendar.monthcalendar(2017, 7))

有些天是 0 值。 这些是与给定月份重叠的一周中的日期,但这是另一个月的一部分。

$ python3 calendar_monthcalendar.py

[[0, 0, 0, 0, 0, 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],
 [31, 0, 0, 0, 0, 0, 0]]

一周的第一天默认为星期一。可以通过调用 setfirstweekday() 来改变它,但由于日历模块包含用于索引到 monthcalendar() 返回的日期范围的常量,因此在这种情况下跳过该步骤更方便。

要计算一年的小组会议日期,假设它们总是在每个月的第二个星期四,请查看 monthcalendar() 的输出以查找星期四下降的日期。本月的第一周和最后一周填充 0 值作为前一个月或后一个月的天数的占位符。 例如,如果一个月在星期五开始,则星期四位置第一周的值将为 0。

calendar_secondthursday.py

import calendar
import sys

year = int(sys.argv[1])

# 显示每月
for month in range(1, 13):

    # 计算与月份重叠的每周的日期
    c = calendar.monthcalendar(year, month)
    first_week = c[0]
    second_week = c[1]
    third_week = c[2]

    # 如果第一周有星期四,
    # 第二个星期四是第二周#。
    # 否则,第二个星期四必须在
    # 第三周。
    if first_week[calendar.THURSDAY]:
        meeting_date = second_week[calendar.THURSDAY]
    else:
        meeting_date = third_week[calendar.THURSDAY]

    print('{:>3}: {:>2}'.format(calendar.month_abbr[month],
                                meeting_date))

所以今年的会议日程是:

$ python3 calendar_secondthursday.py 2017

Jan: 12
Feb:  9
Mar:  9
Apr: 13
May: 11
Jun:  8
Jul: 13
Aug: 10
Sep: 14
Oct: 12
Nov:  9
Dec: 14

也可以看看