Kodewick
第 25 课,共 31 课目录

日期和时间

第 25 课,共 31 课 · 第 4 部分 · 代码和数据 · 约 20 分钟

日期看着简单,可 1 月 15 日加 30 天是哪天、离截止日还有几天,算起来就麻烦了。Python 的 datetime 模块知道每个月有几天、哪年是闰年、哪天星期几,这些都不用你操心。这一课讲它的三种主要类型、日期和文字互相转换、日期加减,以及给自己的代码计时。

造一个日期

datetime 模块里有三种最常用的类型,按名字导入:

类型装的是例子
date某一天date(2026, 3, 14)
datetime某一天的某个时刻datetime(2026, 3, 14, 18, 30)
timedelta一段时间timedelta(days=7)
main.py
1from datetime import date, datetime2d = date(2026, 3, 14)3print(d)4print(d.year, d.month, d.day)5print(d.weekday())6t = datetime(2026, 3, 14, 18, 30)7print(t)8print(t.hour, t.minute)
输出
2026-03-14
2026 3 14
5
2026-03-14 18:30:00
18 30

实参从大到小:年、月、日,再是时、分、秒。显示出来是标准的 ISO 顺序,年在前。weekday() 从星期一算 0,所以 5 是星期六;要中文的星期几,可以用 "一二三四五六日"[d.weekday()]。

不存在的日期会报错,Python 不会替你猜:

main.py
1from datetime import date2print(date(2028, 2, 29))3print(date(2026, 2, 29))
输出
2028-02-29
Traceback (most recent call last):
  File "main.py", line 3, in <module>
    print(date(2026, 2, 29))
ValueError: day is out of range for month

发生了什么: 值不对:类型是对的,但这个值用不了。

2028 年是闰年,2026 年不是,所以第二行抛出 ValueError。

今天和现在

date.today() 得到今天的日期,datetime.now() 得到现在的日期和时间,都是从电脑的时钟读的。

main.py
1from datetime import date, datetime2today = date.today()3now = datetime.now()4print(type(today))5print(type(now))6print(today > date(2020, 1, 1))7print(now > datetime(2020, 1, 1))
输出
<class 'datetime.date'>
<class 'datetime.datetime'>
True
True

这一页没有直接显示它们的值:页面上的输出是网站构建时跑出来的,显示出来的会是那天的日期。在你自己的电脑上,print(today) 和 print(now) 会显示类似这样的东西:

输出
2026-11-01
2026-11-01 09:41:27.318204

秒后面那一段是微秒。now.date() 只留下日期,和 today 一样。这一课后面都用固定的日期,结果每次都一样。

日期变文字:strftime

strftime(string format time)把日期变成你想要的任何样子的文字。每个 % 代码换成日期的一部分,其他字符原样保留。

main.py
1from datetime import datetime2t = datetime(2026, 3, 14, 18, 30)3print(t.strftime("%d/%m/%Y"))4print(t.strftime("%A, %B %d, %Y"))5print(t.strftime("%a %d %b, %I:%M %p"))6print(t.strftime("%Y 年的第 %j 天"))7print(f"{t:%Y年%m月%d日 %H:%M}")8print(t.isoformat())
输出
14/03/2026
Saturday, March 14, 2026
Sat 14 Mar, 06:30 PM
2026 年的第 073 天
2026年03月14日 18:30
2026-03-14T18:30:00
代码意思2026 年 3 月 14 日 18:30
%Y / %y年2026 / 26
%m / %d月 / 日,两位数03 / 14
%B / %b月份名March / Mar
%A / %a星期名Saturday / Sat
%H / %I时,24 小时制 / 12 小时制18 / 06
%M / %S分 / 秒30 / 00
%p上午或下午PM
%j一年里的第几天073

月份名和星期名是英文的;中文日期就像上面那样,把「年月日」直接写进去。这些代码也能写在 f-string 的冒号后面,和第 24 课的格式说明符一样。isoformat() 给出电脑之间交换日期用的标准格式。

文字变日期:strptime

datetime.strptime(string parse time)反过来:给它一段文字和这段文字的格式,它造出一个 datetime。

main.py
1from datetime import datetime2t = datetime.strptime("2026年3月14日 18:30", "%Y年%m月%d日 %H:%M")3print(t)4d = datetime.strptime("2026-03-14", "%Y-%m-%d").date()5print(d)6print(d.strftime("%m月%d日"))
输出
2026-03-14 18:30:00
2026-03-14
03月14日

格式必须和文字一模一样,分隔符也算。对不上就是 ValueError,可以像第 21 课那样接住:

main.py
1from datetime import datetime2try:3    datetime.strptime("14/03/2026", "%Y-%m-%d")4except ValueError as e:5    print(f"日期不对:{e}")
输出
日期不对:time data '14/03/2026' does not match format '%Y-%m-%d'

日期加减

timedelta 是一段时间。日期加上它就往后走,减去它就往前走。月底、闰年 Python 都会处理好。

main.py
1from datetime import date, datetime, timedelta2d = date(2026, 1, 15)3print(d + timedelta(days=30))4print(d - timedelta(weeks=3))5t = datetime(2026, 3, 14, 22, 0)6print(t + timedelta(hours=5))7print(timedelta(hours=36))8print(timedelta(hours=36).total_seconds())
输出
2026-02-14
2025-12-25
2026-03-15 03:00:00
1 day, 12:00:00
129600.0

两个日期相减,得到一个 timedelta。它的 .days 就是相差的天数。

先猜一猜

这段代码会显示什么?

1from datetime import date2gap = date(2026, 3, 1) - date(2026, 2, 1)3print(gap)
main.py
1from datetime import date, datetime2gap = date(2026, 12, 24) - date(2026, 11, 1)3print(gap.days)4shift = datetime(2026, 3, 14, 18, 30) - datetime(2026, 3, 14, 9, 15)5print(shift)6print(shift.total_seconds() / 3600)
输出
53
9:15:00
9.25

改日期的另一个办法是 replace():得到一份把某几部分换掉的副本,比如 d.replace(year=2027)。没有 timedelta(months=1),因为每个月天数不一样;想要「下个月的同一天」,用 replace(month=...)。新的月份里没有这一天时(比如 4 月 31 日),会引发 ValueError。

比较日期

日期的比较和你想的一样:早的小,晚的大。所以 <、==、sorted、min、max 都能用。

main.py
1from datetime import date2deliveries = [date(2026, 5, 2), date(2026, 3, 14), date(2026, 4, 1)]3for d in sorted(deliveries):4    print(d)5print(min(deliveries))6today = date(2026, 4, 10)7late = [d for d in deliveries if d < today]8print(f"已经过了 {len(late)} 个")
输出
2026-03-14
2026-04-01
2026-05-02
2026-03-14
已经过了 2 个

这里的 today 是固定的日期,所以结果不会变。真正的程序里会写 today = date.today()。

给代码计时

想知道一段代码跑了多久,在它前后各读一次 time.perf_counter(),再相减。读出来的数本身没有意义,有意义的只是两次的差。

main.py
1import time2start = time.perf_counter()3total = 04for n in range(100000):5    total += n6elapsed = time.perf_counter() - start7print(total)8print(elapsed > 0)
输出
4999950000
True

每次跑、每台电脑上用的时间都不一样,所以这一页只检查「确实用了一点时间」。在你的电脑上,print(f"用了 {elapsed:.4f} 秒") 会显示类似:

输出
用了 0.0062 秒

计时用 perf_counter,想知道「现在几点」用 datetime.now():perf_counter 更精确,而且改电脑时钟时它不会跳。

练一练

写好代码,点检查。提示随便看,实在想不出来再看答案。

练习 1

写全的日期

把 d 显示成下面这样:年月日,再加中文的星期几。

要显示
2026年07月04日,星期六
exercise1.py
1from datetime import date2d = date(2026, 7, 4)3print(d)
输出
点「运行」或按 Ctrl+Enter
练习 2

还有几天

显示 还有,后面跟着从 today 到 festival 的天数,最后是 天。

要显示
还有 53 天
exercise2.py
1from datetime import date2today = date(2026, 11, 1)3festival = date(2026, 12, 24)4print(festival)
输出
点「运行」或按 Ctrl+Enter
练习 3

十天后到期

读出 text 里的日期,加 10 天,按「年月日」显示出来。

要显示
2026年04月07日
exercise3.py
1from datetime import datetime, timedelta2text = "2026-03-28"3print(text)
输出
点「运行」或按 Ctrl+Enter

这一课学会了

  • from datetime import date, datetime, timedelta:某一天、某一天的某个时刻、一段时间。
  • date(2026, 3, 14) 造一个日期;不存在的日期会抛出 ValueError。
  • date.today() 和 datetime.now() 读电脑的时钟,每次跑都可能不一样。
  • strftime 和 f"{d:%Y年%m月%d日}" 把日期变成文字;strptime 把文字读成 datetime。
  • 日期加 timedelta 还是日期;日期减日期是 timedelta,.days 是天数。
  • 日期按时间先后比较和排序。
  • 在代码前后各读一次 time.perf_counter(),就知道它跑了多久。