📅 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_namecalendar.day_abbr

作用:获取星期名称(英文全称和简称),支持索引访问。

list(calendar.day_name)  # ['Monday', 'Tuesday', ..., 'Sunday']
calendar.day_name[0]     # 'Monday'
calendar.day_abbr[0]     # 'Mon'

🔟 calendar.month_namecalendar.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')
posted @ 2025-08-04 21:41  kyle_7Qc  阅读(77)  评论(0)    收藏  举报