📅 Python 时间管理:datetime 与 calendar 常用方法详解
一、概述
在 Python 中,日期和时间处理主要依赖两个标准库:
datetime:处理时间点、日期时间计算、时区转换、格式化与解析。calendar:处理日历相关操作,如判断闰年、获取某月天数、生成日历矩阵。
这两个模块结合使用,可以解决绝大多数日期、时间和日历场景。
-
星期索引(weekday):
- 0 表示星期一,6 表示星期日。
-
月份索引:
- 1 表示一月,12 表示十二月。
二、📌 datetime 模块
1. 常用函数与方法
| 类/函数 | 方法/属性 | 作用 |
|---|---|---|
| date(year, month, day) | .today() |
获取今天的日期(naive,无时区) |
.fromordinal(n) |
由公历序号(自公元 1-01-01 起)生成日期 | |
.isoformat() |
转换为 ISO 8601 字符串 | |
.weekday() |
返回周几(0=周一,6=周日) | |
.isoweekday() |
返回周几(1=周一,7=周日) | |
.isocalendar() |
返回 (ISO 年, ISO 周, ISO 周内日) |
|
.replace(...) |
替换部分字段生成新对象 | |
| time(hour, minute, second, …) | .isoformat() |
ISO 8601 格式时间 |
.replace(...) |
替换时间字段 | |
| datetime(year, month, day, hour=0, …) | .now([tz]) |
当前本地时间,若传 tz -> aware |
.utcnow() |
当前 UTC 时间(naive,⚠️不含 tzinfo) | |
.fromtimestamp(ts, tz=None) |
时间戳转 datetime | |
.utcfromtimestamp(ts) |
时间戳转 naive UTC | |
.astimezone(tz) |
转换为指定时区 | |
.timestamp() |
转换为时间戳(float 秒) | |
.strftime(fmt) |
按格式输出字符串 | |
.strptime(str, fmt) |
从字符串解析 datetime | |
.isoformat() / .fromisoformat() |
ISO 8601 格式互转 | |
.date() |
取日期部分 | |
.time() |
取时间部分 | |
.replace(...) |
替换字段 | |
| timedelta(days=0, seconds=0, …) | .total_seconds() |
转换为总秒数 |
| timezone(offset, name=None) | .utc |
UTC 时区常量 |
| 辅助 | .combine(date, time) |
合成 datetime |
.min / .max |
最小/最大日期时间 |
2. 示例
2.1 获取当前日期/时间
from datetime import date, datetime, timezone
from zoneinfo import ZoneInfo
today = date.today()
print(today) # 今天日期 2025-08-31
now_local = datetime.now()
print(now_local) # 本地当前时间(naive) 2025-08-31 11:02:41.099034
now_utc = datetime.now(timezone.utc)
print(now_utc)# UTC 当前时间(aware) 2025-08-31 03:02:41.099034+00:00
now_tokyo = now_utc.astimezone(ZoneInfo("Asia/Tokyo")) # 转东京时间
print(now_tokyo) # 2025-08-31 12:02:41.099034+09:00
now_sh = now_utc.astimezone(ZoneInfo("Asia/Shanghai")) # 转上海时间
print(now_sh) # 2025-08-31 11:07:32.555357+08:00
2.2 时间戳互转
from datetime import datetime, timezone
# 本地时间
ts = datetime.now()
print(ts) # 2025-08-31 11:18:38.005607
ts_timestamp = ts.timestamp() # datetime -> 时间戳(秒)
print(ts.timestamp()) # 1756610318.005607
dt = datetime.fromtimestamp(ts_timestamp ,tz=timezone.utc) # 时间戳 -> datetime(UTC)
print(dt) # 2025-08-31 03:18:38.005607+00:00
------------------------------------------------------------
# UTC时间
ts1 = datetime.now(timezone.utc)
print(ts1) # 2025-08-31 03:18:38.005607+00:00
ts1_timestamp = ts1.timestamp() # datetime -> 时间戳(秒)
print(ts1.timestamp()) # 1756610318.005607
dt1 = datetime.fromtimestamp(ts1_timestamp ,tz=timezone.utc) # 时间戳 -> datetime(UTC)
print(dt1) # 2025-08-31 03:18:38.005607+00:00
----
# datetime.timestamp() 永远返回“自 1970-01-01 00:00:00 UTC 以来的秒数”,也就是说,不管你手里的 datetime 对象是哪个时区,它都会被先转换成 UTC,然后再计算 Unix 时间戳。
2.3 日期加减 / 计算时间差
from datetime import datetime, timedelta
dt = datetime(2025, 8, 30, 9, 0)
print(dt,type(dt)) #2025-08-30 09:00:00 <class 'datetime.datetime'>
dt_plus = dt + timedelta(days=7) # 加一周
print(dt_plus,type(dt_plus)) # 2025-09-06 09:00:00 <class 'datetime.datetime'>
delta = dt_plus - dt
print(delta,type(delta)) # 7 days, 0:00:00 <class 'datetime.timedelta'>
print(delta.days) #时间差天数 7
print(delta.total_seconds()) # 时间差秒数 604800.0
2.4 获取星期几 / ISO 周
from datetime import date
d = date(2025, 8, 30)
print(d.weekday()) # 5 -> 周六 (0=周一)
print(d.isocalendar()) # (2025, 35, 6) -> ISO 年/周/周内日
2.5 本周一/周日
from datetime import datetime, timedelta
dt = datetime(2025, 8, 30)
monday = dt - timedelta(days=dt.weekday()) # 本周一
sunday = monday + timedelta(days=6) # 本周日
2.6 本月第一天/最后一天
from datetime import datetime
import calendar
dt = datetime(2025, 8, 30)
first_day = dt.replace(day=1) # 月初
last_day = dt.replace(day=calendar.monthrange(dt.year, dt.month)[1]) # 月末
2.7 求某月第 N 个星期 X
import calendar
from datetime import date
def nth_weekday(year, month, weekday, n):
weeks = calendar.monthcalendar(year, month)
count = 0
for w in weeks:
if w[weekday] != 0:
count += 1
if count == n:
return date(year, month, w[weekday])
print(nth_weekday(2025, 8, calendar.WEDNESDAY, 2)) # 2025-08-13
2.8 格式化与解析
from datetime import datetime
dt = datetime(2025, 8, 30, 9, 5, 6)
s = dt.strftime("%Y-%m-%d %H:%M:%S") # 格式化
dt2 = datetime.strptime(s, "%Y-%m-%d %H:%M:%S") # 解析
2.9 ISO 格式互转(推荐)
from datetime import datetime
dt = datetime(2025, 8, 30, 9, 5, 6)
s = dt.isoformat() # '2025-08-30T09:05:06'
dt2 = datetime.fromisoformat(s) # 解析回来
三、📌 calendar 模块
1、常用函数与方法
| 函数/类 | 方法/属性 | 作用 |
|---|---|---|
| 基础函数 | isleap(year) |
判断是否闰年 |
leapdays(y1, y2) |
统计区间 [y1, y2) 内闰年数 |
|
weekday(y,m,d) |
返回星期几(0=周一,6=周日) | |
monthrange(y,m) |
返回 (当月第一天星期几, 当月天数) |
|
monthcalendar(y,m) |
返回“月历矩阵”,每周 7 天,不在本月的日子为 0 | |
weekheader(n) |
返回一行周标题(宽度 n,例如 "Mo Tu We") |
|
| 文本日历 | TextCalendar(firstweekday=0) |
文本日历对象(0=周一) |
.formatmonth(y,m) |
返回字符串形式的月历 | |
.prmonth(y,m) |
打印月历到终端 | |
.prcal(y) |
打印整年日历 | |
| HTML 日历 | HTMLCalendar(firstweekday=0) |
HTML 日历对象 |
.formatmonth(y,m) |
生成 HTML 表格月历 | |
.formatyear(y) |
生成 HTML 表格全年日历 | |
| 本地化日历 | LocaleTextCalendar/LocaleHTMLCalendar |
按 locale 输出月名、周名 |
| 常量 | calendar.MONDAY ... SUNDAY |
星期常量 0–6 |
calendar.month_name[1..12] |
月份全名 | |
calendar.month_abbr[1..12] |
月份缩写 | |
calendar.day_name[0..6] |
星期全名 | |
calendar.day_abbr[0..6] |
星期缩写 | |
| 设置 | setfirstweekday(n) |
设置周起始(0=周一,6=周日) |
firstweekday() |
返回当前周起始设置 |
2. 示例
2.1 基础示例
import calendar
# 闰年判断
print(calendar.isleap(2024)) # True
# 本月信息
print(calendar.monthrange(2025, 8)) # (4, 31) -> 周五开始,共 31 天
# 月历矩阵
print(calendar.monthcalendar(2025, 8))
# 打印月历
calendar.prmonth(2025, 8)
# HTML 月历
hc = calendar.HTMLCalendar()
print(hc.formatmonth(2025, 8))
2.2 日历相关(calendar)
import calendar
print(calendar.isleap(2024)) # 闰年? True
print(calendar.monthrange(2025, 8)) # (4, 31) -> 月首是周五, 31天
print(calendar.weekday(2025, 8, 30)) # 5 -> 周六
print(calendar.monthcalendar(2025, 8)) # 返回矩阵 [[0,0,0,1,2,...], ...]
calendar.prmonth(2025, 8) # 打印月历
calendar.prcal(2025) # 打印整年月历
2.3 HTML 日历(网页场景)
import calendar
hc = calendar.HTMLCalendar(firstweekday=calendar.SUNDAY)
html = hc.formatmonth(2025, 8)
print(html) # HTML 表格,可直接放到网页
2.4 常用函数详解(配示例)
1️⃣ calendar.month(year, month)
作用:返回一个多行字符串形式的指定月份日历。
import calendar
print(calendar.month(2025, 8))
📌 输出:
August 2025
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
2️⃣ calendar.calendar(year, w=2, l=1, c=6)
作用:返回一个多行字符串形式的全年日历。
w: 每个日期的宽度l: 每一周之间的行数c: 每个月之间的间距
print(calendar.calendar(2025))
3️⃣ calendar.isleap(year)
作用:判断某一年是否为闰年。返回布尔值。
calendar.isleap(2024) # True
calendar.isleap(2025) # False
4️⃣ calendar.leapdays(y1, y2)
作用:返回在区间 [y1, y2) 中闰年的数量。
calendar.leapdays(2000, 2025) # 6
📌 注意:不包括 y2 本身
5️⃣ calendar.weekday(year, month, day)
作用:返回指定日期的星期几,0 表示星期一。
calendar.weekday(2025, 8, 4) # 0 (星期一)
6️⃣ calendar.monthrange(year, month)
作用:返回一个元组 (start_day, total_days):
start_day是该月 1 号是星期几(0=周一)total_days是该月的天数
calendar.monthrange(2025, 8) # (4, 31)
7️⃣ calendar.monthcalendar(year, month)
作用:返回该月的日历,格式为二维列表。每一子列表代表一周,0 表示该位置无效(即不属于该月的日期)。
calendar.monthcalendar(2025, 8)
📌 输出样例:
[
[0, 0, 0, 0, 1, 2, 3],
[4, 5, 6, 7, 8, 9,10],
[11,12,13,14,15,16,17],
...
]
8️⃣ calendar.setfirstweekday(n)
作用:设置每周的起始星期日。默认是 0(星期一)。
calendar.setfirstweekday(6) # 以周日为第一天
print(calendar.month(2025, 8))
9️⃣ calendar.day_name 和 calendar.day_abbr
作用:获取星期名称(英文全称和简称),支持索引访问。
list(calendar.day_name) # ['Monday', 'Tuesday', ..., 'Sunday']
calendar.day_name[0] # 'Monday'
calendar.day_abbr[0] # 'Mon'
🔟 calendar.month_name 和 calendar.month_abbr
作用:获取月份英文名(全称/简称),索引 1-12 表示月份。
list(calendar.month_name) # ['', 'January', ..., 'December']
calendar.month_name[8] # 'August'
calendar.month_abbr[8] # 'Aug'
📌 注意:索引 0 是空字符串。
四、场景示例
1. 每月第一个星期一
# 基础版
import pandas as pd
from datetime import datetime, timedelta
import calendar
def get_first_monday_of_month(year, month):
first_day = datetime(year, month, 1)
if first_day.weekday() == 0:
return first_day
else:
days_to_add = (0 - first_day.weekday()) % 7
return first_day + timedelta(days=days_to_add)
# def get_first_monday(year, month):
# # 获取该月第一天是星期几(0=Monday, 6=Sunday)
# first_day_weekday = calendar.weekday(year, month, 1)
# # 计算需要加几天才能到下一个星期一
# days_to_add = (0 - first_day_weekday) % 7
# return datetime(year, month, 1) + timedelta(days=days_to_add)
# 当前时间
now = datetime.now()
start_year = now.year
start_month = now.month
restart_times = []
for i in range(12):
year = start_year + (start_month + i - 1) // 12
month = (start_month + i - 1) % 12 + 1
first_monday = get_first_monday_of_month(year, month)
restart_time = first_monday.replace(hour=6, minute=31, second=0, microsecond=0)
restart_times.append(restart_time.strftime("%Y-%m-%d %H:%M"))
# 创建 DataFrame
df = pd.DataFrame(restart_times, columns=["Restart Time"])
# 导出到 Excel
output_file = "Windows-Restarted.xlsx"
df.to_excel(output_file, index=False)
print(f"Excel 文件已生成:{output_file}")
2. 每天凌晨一点
1. 从当前时间开始
from datetime import datetime,timedelta
from openpyxl import load_workbook
import calendar
def get_time(year,month,day):
dt=datetime(year,month,day)
return datetime.strftime((dt + timedelta(hours=1)),'%Y%m%d%H%M%S')
start_year=datetime.now().year
start_month=datetime.now().month
start_day=datetime.now().day
dt=[]
for i in range(12):
year = start_year + (start_month + i - 1) // 12
month = (start_month + i - 1) % 12 + 1
# 获取该月的实际天数
_, num_days = calendar.monthrange(year, month)
for j in range(num_days):
day = j + 1 # 生成从1到num_days的天数
time = get_time(year, month, day)
dt.append(time)
data_str = ",".join(dt)
# #
# print(data_str)
# 已有文件
wb = load_workbook('time.xlsx')
ws = wb['Sheet1']
for i in range(1,319):
if ws[f'k{i}'].value == "凌晨1点(test)":
ws[f'L{i}'] = data_str
wb.save('time.xlsx')
print('done')
--------------------------------------------------------------------------------------------
2. 从指定时间开始,每天凌晨一点
from datetime import datetime,timedelta
from openpyxl import load_workbook
import calendar
def get_time(year,month,day):
dt=datetime(year,month,day)
return datetime.strftime((dt + timedelta(hours=1)),'%Y%m%d%H%M%S')
start_year=datetime.now().year
start_month=6
start_day=1
dt=[]
for i in range(3):
year = start_year + (start_month + i - 1) // 12
month = (start_month + i - 1) % 12 + 1
# 获取该月的实际天数
_, num_days = calendar.monthrange(year, month)
for j in range(num_days):
day = j + 1 # 生成从1到num_days的天数
time = get_time(year, month, day)
dt.append(time)
data_str = ",".join(dt)
# #
# print(data_str)
# 已有文件
wb = load_workbook('time.xlsx')
ws = wb['Sheet1']
for i in range(1,319):
if ws[f'k{i}'].value == "凌晨1点":
ws[f'L{i}'] = data_str
wb.save('time.xlsx')
print('done')
3. 每月第二个周三晚8点
# 指定时间开始,指定时间结束
from datetime import datetime,timedelta
from openpyxl import load_workbook
def get_time(year,month):
dt=datetime(year,month,1)
if dt.weekday() == 2:
return datetime.strftime(dt + timedelta(days=7,hours=20),'%Y%m%d%H%M%S')
else:
return datetime.strftime((dt + timedelta(days=((2-dt.weekday())%7+7),hours=20)),'%Y%m%d%H%M%S')
start_year=datetime.now().year
start_month=6
dt=[]
for i in range(3):
year = start_year + (start_month + i-1 ) // 12
month = (start_month + i-1) %12 +1
time = get_time(year,month)
dt.append(time)
data_str = ",".join(dt)
# 写入到Excel
# 文件不存在
# wb = Workbook()
# ws = wb.active
# ws['A1'] = data_str
# wb.save('output.xlsx')
# 已有文件
wb = load_workbook('time.xlsx')
ws = wb['Sheet1']
for i in range(1,319):
if ws[f'k{i}'].value == "第二个周三晚8点":
ws[f'L{i}'] = data_str
wb.save('time.xlsx')
print('done')
4. 每季度第一周三国内晚7点
# 指定时间开始,指定时间结束
from datetime import datetime,timedelta
from openpyxl import load_workbook
import calendar
def get_time(year,month):
dt=datetime(year,month,1)
if dt.weekday() == 2:
return datetime.strftime(dt + timedelta(hours=19),'%Y%m%d%H%M%S')
else:
return datetime.strftime((dt + timedelta(days=((2-dt.weekday())%7),hours=19)),'%Y%m%d%H%M%S')
dt=[]
for y in [2025,2026,2027]:
for m in [1,4,7,10]:
time = get_time(y, m)
dt.append(time)
data_str = ",".join(dt)
# #
print(data_str)
#
# 已有文件
wb = load_workbook('time.xlsx')
ws = wb['Sheet1']
for i in range(1,319):
if ws[f'k{i}'].value == "第一周三国内晚7-10点" or ws[f'k{i}'].value == "第一周三国内晚7-10点(owner手动重启)":
ws[f'L{i}'] = data_str
wb.save('time.xlsx')
print('done')
5. 每月1号1点
# 指定时间,从几月开始,到几月结束,几号,几点
from datetime import datetime,timedelta
from openpyxl import load_workbook
import calendar
def get_time(year,month):
dt=datetime(year,month,1)
return datetime.strftime((dt + timedelta(hours=1)),'%Y%m%d%H%M%S')
start_year=datetime.now().year
start_month=6
dt=[]
for i in range(3):
year = start_year + (start_month + i - 1) // 12
month = (start_month + i - 1) % 12 + 1
time = get_time(year, month)
dt.append(time)
data_str = ",".join(dt)
# print(data_str)
# 已有文件
wb = load_workbook('time.xlsx')
ws = wb['Sheet1']
for i in range(1,319):
if ws[f'k{i}'].value == "每月1号1点":
ws[f'L{i}'] = data_str
wb.save('time.xlsx')
print('done')
6. 每月的每周x的y点
from datetime import datetime, timedelta
import calendar
from openpyxl import load_workbook
def get_monthly_mondays(year, month):
first_day = datetime(year, month, 1)
last_day = datetime(year, month, calendar.monthrange(year, month)[1])
mondays = []
current_day = first_day
while current_day <= last_day:
if current_day.weekday() == 0: # 0表示星期一
monday = current_day.replace(hour=2)
mondays.append(monday.strftime('%Y%m%d%H%M%S'))
current_day += timedelta(days=1)
return mondays
def main():
current_year = datetime.now().year
current_month = 6
dates = []
for i in range(3):
year = current_year + (current_month + i - 1) // 12
month = (current_month + i - 1) % 12 + 1
mondays = get_monthly_mondays(year, month)
dates.extend(mondays)
data_str = ",".join(dates)
return data_str
if __name__ == "__main__":
wb = load_workbook('time.xlsx')
ws = wb['Sheet1']
for i in range(1, 319):
if ws[f'k{i}'].value == "每周一2点":
ws[f'L{i}'] = main()
wb.save('time.xlsx')
print('done')
7. 每月最后一个周x
from datetime import datetime, timedelta
import calendar
from openpyxl import load_workbook
def get_monthly_mondays(year, month):
# 获取该月的最后一天
_, last_day_of_month = calendar.monthrange(year, month)
# 构造该月最后一天的日期对象
last_day = datetime(year, month, last_day_of_month)
if last_day.weekday() == 6:
return datetime.strftime(last_day+timedelta(hours=4),'%Y%m%d%H%M%S')
else:
return datetime.strftime(last_day-timedelta(days=(last_day.weekday()-6)%7) +timedelta(hours=4),'%Y%m%d%H%M%S')
def main():
current_year = datetime.now().year
current_month = 6
dates = []
for i in range(3):
year = current_year + (current_month + i - 1) // 12
month = (current_month + i - 1) % 12 + 1
mondays = get_monthly_mondays(year, month)
dates.append(mondays)
data_str = ",".join(dates)
return data_str
if __name__ == "__main__":
wb = load_workbook('1Windows-Restarted.xlsx')
ws = wb['Sheet1']
for i in range(1, 319):
if ws[f'k{i}'].value == "time.xlsx":
ws[f'L{i}'] = main()
wb.save('time.xlsx')
print('done')

浙公网安备 33010602011771号