一、引言

Python 作为一门功能强大且应用广泛的编程语言,凭借其丰富的库和工具生态系统,在众多领域发挥着重要作用。无论是 Web 开发、数据分析与科学、机器学习与人工智能,还是桌面自动化、爬虫脚本、金融量化交易以及教育研究等领域,Python 都展现出了卓越的适应性和高效性。
在日常开发中,时间和日期的处理是一个常见且复杂的任务。Python 标准库中的 datetime
和 time
模块提供了基本的时间处理功能,但它们的 API 设计不够直观,使用起来较为繁琐,而且在处理时区、本地化和相对时间计算等方面存在一定的局限性。为了解决这些问题,第三方库 pendulum
应运而生,它提供了更加优雅、直观且功能强大的 API,让时间处理变得轻松愉快。
二、pendulum 概述
2.1 用途
pendulum
是一个致力于简化 Python 时间处理的库。它在 Python 标准库的基础上进行了扩展,提供了更加直观、易用的 API,使得时间和日期的创建、操作、格式化和时区转换等任务变得异常简单。无论是处理相对时间(如 “3 天前”、”2 周后”)、时区转换、日期比较,还是进行复杂的时间计算,pendulum
都能轻松应对。
2.2 工作原理
pendulum
的核心是围绕 DateTime
类构建的,该类继承自 Python 标准库中的 datetime.datetime
类,但提供了更多的方法和功能。它通过封装底层的时间处理逻辑,提供了流畅的链式调用 API,让代码更加简洁易读。例如,你可以轻松地创建一个日期时间对象,然后通过链式调用进行各种操作:
import pendulum
# 创建一个表示当前时间的对象
now = pendulum.now()
# 将时间调整为明天,并格式化为 ISO 8601 字符串
tomorrow_iso = now.add(days=1).to_iso8601_string()
# 计算从现在到明天的时间差
diff = now.diff(now.add(days=1))
print(diff.in_hours()) # 输出 24
2.3 优缺点
优点:
- 直观的 API:
pendulum
的 API 设计非常直观,易于理解和使用,减少了开发者的学习成本。 - 时区支持:内置了强大的时区支持,使得时区转换变得简单明了。
- 相对时间处理:提供了简洁的方式来处理相对时间,如 “昨天”、”下周” 等。
- 链式调用:支持流畅的链式调用语法,使代码更加简洁易读。
- 兼容性:与 Python 标准库的
datetime
模块完全兼容,可以无缝集成到现有项目中。
缺点:
- 额外依赖:作为第三方库,使用时需要额外安装,增加了项目的依赖管理成本。
- 学习曲线:对于已经熟悉 Python 标准库的开发者来说,需要一定的时间来适应
pendulum
的 API 风格。
2.4 License 类型
pendulum
采用 MIT License,这是一种非常宽松的开源许可证,允许用户自由使用、修改和分发代码,只需保留原作者的版权声明即可。这种许可证对于商业和非商业项目都非常友好。
三、pendulum 详细使用指南
3.1 安装 pendulum
使用 pip
可以轻松安装 pendulum
:
pip install pendulum
安装完成后,就可以在 Python 代码中导入并使用它了。
3.2 创建日期和时间对象
3.2.1 获取当前时间
使用 pendulum.now()
可以获取当前时间的 DateTime
对象:
import pendulum
now = pendulum.now()
print(now) # 输出当前时间,例如:2025-06-04T14:30:00+08:00
你还可以指定时区:
now_in_utc = pendulum.now('UTC')
print(now_in_utc) # 输出 UTC 时区的当前时间
3.2.2 创建指定日期和时间
可以使用多种方式创建指定的日期和时间:
# 创建一个指定年月日的日期对象
dt = pendulum.datetime(2025, 6, 4)
print(dt) # 2025-06-04T00:00:00+00:00
# 创建一个指定年月日时分秒的日期对象
dt = pendulum.datetime(2025, 6, 4, 14, 30, 59)
print(dt) # 2025-06-04T14:30:59+00:00
# 从时间戳创建
dt = pendulum.from_timestamp(1643872259)
print(dt) # 2022-01-30T14:30:59+00:00
# 从字符串解析
dt = pendulum.parse('2025-06-04T14:30:59')
print(dt) # 2025-06-04T14:30:59+00:00
pendulum.parse()
方法非常灵活,可以解析多种格式的日期字符串:
dt = pendulum.parse('2025-06-04')
print(dt) # 2025-06-04T00:00:00+00:00
dt = pendulum.parse('2025/06/04 14:30')
print(dt) # 2025-06-04T14:30:00+00:00
dt = pendulum.parse('June 4, 2025', strict=False)
print(dt) # 2025-06-04T00:00:00+00:00
3.2.3 创建相对时间
pendulum
提供了简洁的方式来创建相对时间:
# 创建昨天的日期
yesterday = pendulum.yesterday()
print(yesterday) # 2025-06-03T00:00:00+00:00
# 创建明天的日期
tomorrow = pendulum.tomorrow()
print(tomorrow) # 2025-06-05T00:00:00+00:00
# 创建今天开始的时间
today_start = pendulum.today()
print(today_start) # 2025-06-04T00:00:00+00:00
3.3 操作日期和时间
3.3.1 加减时间
使用 add()
和 subtract()
方法可以轻松地对日期和时间进行加减操作:
now = pendulum.now()
# 增加一天
tomorrow = now.add(days=1)
# 减少一小时
an_hour_ago = now.subtract(hours=1)
# 链式调用:增加2天3小时45分钟
future = now.add(days=2, hours=3, minutes=45)
# 也可以使用快捷方法
next_week = now.next(pendulum.MONDAY) # 下一个星期一
last_month = now.subtract(months=1)
3.3.2 修改特定部分
可以直接修改日期和时间的特定部分:
dt = pendulum.now()
# 修改年份
dt = dt.set(year=2026)
# 同时修改月、日、小时
dt = dt.set(month=12, day=25, hour=18)
print(dt) # 输出修改后的日期时间
3.3.3 比较日期和时间
pendulum
对象可以直接进行比较:
dt1 = pendulum.datetime(2025, 6, 4)
dt2 = pendulum.datetime(2025, 6, 5)
print(dt1 < dt2) # True
print(dt1 == dt2) # False
print(dt1 > dt2) # False
# 检查是否在某个时间段内
now = pendulum.now()
start = now.subtract(days=1)
end = now.add(days=1)
print(now.between(start, end)) # True
3.4 时区处理
pendulum
内置了强大的时区支持:
# 创建一个带有时区的日期时间对象
dt = pendulum.datetime(2025, 6, 4, 14, 30, tz='Asia/Shanghai')
print(dt) # 2025-06-04T14:30:00+08:00
# 时区转换
dt_in_utc = dt.in_timezone('UTC')
print(dt_in_utc) # 2025-06-04T06:30:00+00:00
# 获取当前时区
local_tz = pendulum.local_timezone()
print(local_tz) # 输出当前系统时区,如 Asia/Shanghai
3.5 格式化和解析
3.5.1 格式化为字符串
pendulum
对象可以方便地格式化为各种字符串:
dt = pendulum.now()
# 格式化为 ISO 8601 字符串
iso_str = dt.to_iso8601_string()
print(iso_str) # 例如:2025-06-04T14:30:00+08:00
# 格式化为 RFC 2822 字符串
rfc_str = dt.to_rfc2822_string()
print(rfc_str) # 例如:Wed, 04 Jun 2025 14:30:00 +0800
# 使用自定义格式
custom_str = dt.format('YYYY-MM-DD HH:mm:ss')
print(custom_str) # 例如:2025-06-04 14:30:00
# 本地化格式
fr_str = dt.format('LLLL', locale='fr')
print(fr_str) # 例如:mercredi 4 juin 2025 14:30
3.5.2 从字符串解析
前面已经介绍过 pendulum.parse()
方法,它还支持指定时区和严格模式:
# 解析带时区的字符串
dt = pendulum.parse('2025-06-04T14:30:00+08:00')
print(dt) # 2025-06-04T14:30:00+08:00
# 指定时区解析
dt = pendulum.parse('2025-06-04 14:30:00', tz='Asia/Shanghai')
print(dt) # 2025-06-04T14:30:00+08:00
# 严格模式
try:
dt = pendulum.parse('June 4, 2025', strict=True)
except ValueError:
print("解析失败,因为严格模式下无法识别该格式")
dt = pendulum.parse('June 4, 2025', strict=False)
print(dt) # 2025-06-04T00:00:00+00:00
3.6 时间差计算
计算两个时间点之间的差值是时间处理中的常见需求,pendulum
提供了简单而强大的方法:
dt1 = pendulum.datetime(2025, 6, 1)
dt2 = pendulum.datetime(2025, 6, 10)
# 计算时间差
delta = dt2 - dt1
print(delta.days) # 9
# 使用 diff() 方法
delta = dt1.diff(dt2)
print(delta.in_days()) # 9
print(delta.in_hours()) # 216
# 相对时间表示
print(delta.for_humans()) # 9 days
for_humans()
方法非常实用,可以将时间差转换为人类可读的格式:
now = pendulum.now()
future = now.add(days=3, hours=2, minutes=15)
delta = future - now
print(delta.for_humans()) # 3 days 2 hours 15 minutes
past = now.subtract(weeks=1)
delta = now - past
print(delta.for_humans()) # 1 week ago
3.7 周期和迭代
pendulum
可以创建时间周期,并对其进行迭代:
start = pendulum.datetime(2025, 1, 1)
end = pendulum.datetime(2025, 1, 10)
# 创建一个周期
period = pendulum.period(start, end)
# 迭代周期内的每一天
for dt in period.range('days'):
print(dt.to_date_string())
# 计算周期内的天数
print(period.days) # 9
# 检查某个时间点是否在周期内
print(pendulum.datetime(2025, 1, 5) in period) # True
四、实际案例
4.1 任务调度系统中的时间计算
假设你正在开发一个任务调度系统,需要根据用户设置的时间间隔来安排任务执行。以下是一个使用 pendulum
的示例:
import pendulum
from typing import List, Dict
class TaskScheduler:
def __init__(self):
self.tasks = []
def add_task(self, name: str, interval: Dict[str, int], start_time: pendulum.DateTime = None):
"""添加一个定时任务
Args:
name: 任务名称
interval: 时间间隔,如 {"days": 1, "hours": 2}
start_time: 开始时间,默认为当前时间
"""
if start_time is None:
start_time = pendulum.now()
task = {
"name": name,
"interval": interval,
"start_time": start_time,
"next_run": start_time
}
self.tasks.append(task)
def get_next_tasks(self, count: int = 5) -> List[Dict]:
"""获取接下来要执行的任务
Args:
count: 获取的任务数量
Returns:
排序后的任务列表
"""
# 按下次执行时间排序
sorted_tasks = sorted(self.tasks, key=lambda t: t["next_run"])
return sorted_tasks[:count]
def execute_task(self, task: Dict):
"""执行任务并更新下次执行时间"""
print(f"执行任务: {task['name']}")
# 更新下次执行时间
next_run = task["next_run"].add(**task["interval"])
task["next_run"] = next_run
def run_pending(self):
"""运行所有待执行的任务"""
now = pendulum.now()
for task in self.tasks:
if task["next_run"] <= now:
self.execute_task(task)
# 使用示例
scheduler = TaskScheduler()
# 添加任务
scheduler.add_task(
"每日数据备份",
{"days": 1},
pendulum.datetime(2025, 6, 4, 23, 59) # 每天 23:59 执行
)
scheduler.add_task(
"每周报告生成",
{"weeks": 1},
pendulum.datetime(2025, 6, 7, 15, 0) # 每周日 15:00 执行
)
scheduler.add_task(
"每小时监控检查",
{"hours": 1}
)
# 查看接下来要执行的任务
next_tasks = scheduler.get_next_tasks()
for task in next_tasks:
print(f"任务: {task['name']}, 下次执行时间: {task['next_run']}")
# 运行待执行的任务
scheduler.run_pending()
4.2 数据分析中的时间序列处理
在数据分析中,经常需要处理时间序列数据。以下是一个使用 pendulum
处理时间序列数据的示例:
import pendulum
import random
import pandas as pd
import matplotlib.pyplot as plt
# 生成模拟时间序列数据
def generate_time_series(start_date: str, end_date: str, freq: str = 'D') -> pd.DataFrame:
"""生成模拟时间序列数据
Args:
start_date: 开始日期
end_date: 结束日期
freq: 频率,如 'D' 表示天,'H' 表示小时
Returns:
包含时间序列数据的 DataFrame
"""
# 创建日期范围
date_range = pd.date_range(start=start_date, end=end_date, freq=freq)
# 生成随机数据
values = [random.randint(100, 1000) for _ in range(len(date_range))]
# 创建 DataFrame
df = pd.DataFrame({
'date': date_range,
'value': values
})
return df
# 分析时间序列数据
def analyze_time_series(df: pd.DataFrame) -> None:
"""分析时间序列数据并可视化
Args:
df: 包含时间序列数据的 DataFrame
"""
# 将日期列转换为 pendulum 日期
df['pendulum_date'] = df['date'].apply(lambda x: pendulum.instance(x.to_pydatetime()))
# 提取月份和星期几
df['month'] = df['pendulum_date'].apply(lambda x: x.month_name())
df['day_of_week'] = df['pendulum_date'].apply(lambda x: x.day_of_week)
# 按月份分组统计
monthly_stats = df.groupby('month')['value'].sum().reset_index()
# 按星期几分组统计
weekday_stats = df.groupby('day_of_week')['value'].mean().reset_index()
# 转换星期几为名称
weekday_names = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
weekday_stats['day_name'] = weekday_stats['day_of_week'].apply(lambda x: weekday_names[x])
# 可视化
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
# 月度统计
ax1.bar(monthly_stats['month'], monthly_stats['value'])
ax1.set_title('月度统计')
ax1.set_xlabel('月份')
ax1.set_ylabel('总和')
ax1.tick_params(axis='x', rotation=45)
# 周度统计
ax2.bar(weekday_stats['day_name'], weekday_stats['value'])
ax2.set_title('周度统计')
ax2.set_xlabel('星期')
ax2.set_ylabel('平均值')
plt.tight_layout()
plt.show()
# 使用示例
# 生成一年的每日数据
df = generate_time_series('2024-01-01', '2024-12-31')
# 分析数据
analyze_time_series(df)
五、总结
pendulum
是一个功能强大、使用方便的 Python 时间处理库,它弥补了 Python 标准库在时间处理方面的不足,提供了更加直观、优雅的 API。通过本文的介绍,我们了解了 pendulum
的基本概念、工作原理、优缺点以及详细的使用方法,并通过实际案例展示了它在任务调度和数据分析等场景中的应用。
无论是处理简单的日期计算,还是复杂的时区转换和时间序列分析,pendulum
都能帮助你轻松应对。如果你还在为 Python 中的时间处理而烦恼,不妨尝试一下 pendulum
,相信它会给你带来惊喜。
六、相关资源
- Pypi地址:https://pypi.org/project/pendulum
- Github地址:https://github.com/sdispater/pendulum
- 官方文档地址:https://pendulum.eustace.io/docs/
关注我,每天分享一个实用的Python自动化工具。
