跳至主要内容

Python timedelta:在 Python 中处理时间间隔

在 Python 中,timedelta 是 datetime 模块中的一种数据类型,用于表示持续时间或两个时间点之间的差值。
· 9分钟 阅读

使用 AI 探索

许多真实世界的数据集都包含日期和时间,数据科学中的常见操作之一就是计算两个时间点之间的时间差。

我们用来度量日期和时间的单位(年、月、日、小时、分钟、秒)并不总是好用,尤其是在这些单位长度不规则的情况下。

不过,Python 的 timedelta 数据类型(datetime 模块的一部分)可以处理计算时间间隔时的各种复杂情况。

我们先给出一个非常简短的答案来说明什么是 Python 的 timedelta,然后再展开介绍更多细节。

想了解更多有关 Python 中日期和时间的内容,请查看课程 Working With Dates and Time in Python。

简答:什么是 Python 的 timedelta

datetime 模块中最重要、最常用的两种数据类型是 timedelta 和 datetime。datetime 对象表示时间上的具体时刻(例如:2024 年 1 月 1 日上午 10:30),而 timedelta 对象表示两个时刻之间的持续时间或间隔。可以把它理解为回答“何时?”(datetime)与“多长时间?”(timedelta)之间的区别。

考虑两个日期,我们可以用两个 datetime 对象来表示。将一个日期减去另一个日期会生成一个 timedelta 对象,表示这两个日期之间的时间间隔:

import datetime

first_date = datetime.datetime(year=2024, month=1, day=1)
second_date = datetime.datetime(year=2024, month=5, day=27)
interval = second_date - first_date
print(interval)
147 days, 0:00:00

输出表明这两个日期正好相隔 147 天。下例中,我们通过调用 datetime.datetime.now() 使用代码执行时的日期和时间:

first_date = datetime.datetime(year=2024, month=1, day=1)
second_date = datetime.datetime.now()
interval = second_date - first_date

print(second_date)
print(interval)
2024-05-28 08:32:44.871646
148 days, 8:32:44.871646

timedelta 对象现在显示的是从 2024 年开始到代码执行时的日期和时间之间所经过的时间。

我们将在本文中进一步探索 Python 的 timedelta。

创建 timedelta 对象

在上一节中,我们从两个 datetime 对象创建了一个 timedelta 对象,但我们也可以通过直接调用 timedelta() 构造函数来创建时间间隔:

interval = datetime.timedelta(days=10, seconds=3600)
print(interval)
10 days, 1:00:00

该间隔为十天一小时。创建 timedelta 实例时,我们也可以使用其他时间单位:

interval = datetime.timedelta(weeks=1, days=3, hours=1)
print(interval)
10 days, 1:00:00

调用 timedelta() 时,可以使用以下任意时间单位:

  • weeks
  • days
  • hours
  • minutes
  • seconds
  • milliseconds
  • microseconds

本教程将更深入地介绍 timedelta 对象如何存储时间间隔。

timedelta 的基础运算

我们还可以对 timedelta 对象执行算术运算。考虑下面三个 datetime 对象,分别表示 Python 1.0、Python 2.0 和 Python 3.0 的发布日期。将 datetime 对象相减会创建一个 timedelta 对象:

release_date_python_1 = datetime.datetime(year=1991, month=2, day=20)
release_date_python_2 = datetime.datetime(year=2000, month=10, day=16)
release_date_python_3 = datetime.datetime(year=2008, month=12, day=3)

​time_between_1_and_2 = release_date_python_2 - release_date_python_1
time_between_2_and_3 = release_date_python_3 - release_date_python_2

​print(time_between_1_and_2)
print(time_between_2_and_3)
3526 days, 0:00:00
2970 days, 0:00:00

输出显示了 Python 1.0 与 2.0 发布之间,以及 2.0 与 3.0 发布之间的天数。这些时间间隔都是 timedelta 对象。

还可以在 timedelta 对象和 datetime 对象之间进行加减运算。我们给 release_date_python_3 加上一个 days=100 的 timedelta:

print(release_date_python_3 + datetime.timedelta(days=100))
2009-03-13 00:00:00

Python 3.0 发布于 2008 年 12 月 3 日。这个日期加上一百天就是 2009 年 3 月 13 日。

不过,我们也可以直接对 timedelta 对象进行运算:

print("Difference between gap 1 and gap 2")
print(time_between_1_and_2 - time_between_2_and_3)

print("Sum of gap 1 and gap 2")
print(time_between_1_and_2 + time_between_2_and_3)
Difference between gap 1 and gap 2
556 days, 0:00:00

Sum of gap 1 and gap 2
6496 days, 0:00:00

输出验证了可以对 timedelta 对象进行减法和加法。乘法和除法同样可行:

print("Multiply time interval by 3:")
print(time_between_1_and_2 * 3)

print("Divide time interval by 3:")
print(time_between_1_and_2 / 3)
Multiply time interval by 3:
10578 days, 0:00:00

Divide time interval by 3:
1175 days, 8:00:00

我们还可以比较 timedelta 对象:

print("Check if gap 1 is greater than gap 2")
print(time_between_1_and_2 > time_between_2_and_3)
True

输出表明,Python 1.0 与 Python 2.0 的发布时间间隔长于 Python 2.0 与 Python 3.0 之间的时间间隔。

关键的 timedelta 属性与方法

timedelta 对象通过存储天、秒和微秒的数量来表示时间间隔。这三种单位是 timedelta 类中唯一以属性形式表示的时间单位:

interval = datetime.timedelta(
    weeks=1,
    hours=10,
    minutes=22,
    milliseconds=1042,
)

print(f"{interval = }")
print(f"{interval.days = }")
print(f"{interval.seconds = }")
print(f"{interval.microseconds = }")
interval = datetime.timedelta(days=7, seconds=37321, microseconds=42000)
interval.days = 7
interval.seconds = 37321
interval.microseconds = 42000

尽管对 timedelta() 的调用包含了周、小时、分钟和毫秒等参数,构造函数会将这些单位转换为天、秒和微秒。上述 print() 调用使用带等号 = 的 f-string 来显示变量名及其值。您可以在这篇教程 f-string formatting in Python 中了解更多关于 f-string 的信息。

timedelta 类的另一个有用工具是 total_seconds() 方法。该方法返回整个时间间隔的总秒数:

print("Total time interval in seconds:")
print(interval.total_seconds())
Total time interval in seconds:
642121.042

此值表示整个时间间隔的秒数。它不同于 .seconds 的值,后者只显示间隔中的“秒”组成部分,不包含天和微秒。

我们可以使用 .total_seconds() 返回的值将时间间隔转换为任何其他单位:

print("Total time interval in days:")
print(interval.total_seconds() / 3600 / 24)
Total time interval in days:
7.431956504629629

输出显示了以天为单位的时间间隔,因为我们将 .total_seconds() 的返回值先除以每小时的秒数,再除以每天的小时数。

timedelta 示例:计算增长

我们来看一个例子。一家销售 Python 书籍的在线商店会记录所有销售数据。以下是一位客户的数据:

data = {
    "customer_id": 3542,
    "sales": [
        {"date": "2024-04-21T13:23:45", "price": 24.99, "quantity": 1},
        {"date": "2024-02-13T10:54:12", "price": 24.99, "quantity": 2},
        {"date": "2024-01-08T20:32:24", "price": 18.99, "quantity": 1},
    ],
}

我们需要计算该客户第一次下单与最后一次下单之间的时间差。订单列表中的记录始终按时间顺序排列。

第一步是从这个嵌套结构中提取日期,并将其转换为 datetime 对象。幸好,这些时间戳采用标准的 ISO 8601 格式(YYYY-MM-DDTHH:MM:SS),因此可以使用便捷的 fromisoformat() 方法:

sales = data["sales"]
dates = []
for item in sales:
    date = datetime.datetime.fromisoformat(item["date"])
    dates.append(date)

print(dates)
[datetime.datetime(2024, 4, 21, 13, 23, 45), datetime.datetime(2024, 2, 13, 10, 54, 12), datetime.datetime(2024, 1, 8, 20, 32, 24)]

列表 dates 包含了每笔订单日期对应的 datetime 对象。最后,我们可以计算第一单与最后一单之间的时间间隔:

time_span = dates[0] - dates[-1]
print(time_span)
print(time_span.days)
103 days, 16:51:21
103

两个日期相减得到一个 timedelta 对象。代码打印的 time_span 展示了天、小时、分钟和秒的组合。我们也打印 time_span.days 来仅显示天数的整数部分。

如果您想了解更多将字符串转换为 datetime 的方法,请查看这篇教程:Python String to Datetime。

使用 timedelta 的快速提示

在使用 Python 的 timedelta 时,请注意以下几点:

1. 调用 timedelta() 时最好使用关键字参数,以避免单位出错:

interval = datetime.timedelta(weeks=1, days=3, hours=1, milliseconds=1354)

2. 当要计算位于不同时区的两个日期之间的时间间隔时,timedelta 会先把日期转换为 UTC,然后再计算时间差:

import zoneinfo

some_date = datetime.datetime.fromisoformat("2024-01-01T00:00:00")

some_date_lon = some_date.replace(tzinfo=zoneinfo.ZoneInfo("Europe/London"))
some_date_nyc = some_date.replace(tzinfo=zoneinfo.ZoneInfo("America/New_York"))

print(some_date_lon)
print(some_date_nyc)
print(some_date_nyc - some_date_lon)
2024-01-01 00:00:00+00:00
2024-01-01 00:00:00-05:00
5:00:00

some_date 中的日期表示新年的开始。由于它没有时区信息,这是一个“天真”的 datetime 对象。而 some_date_lon 和 some_date_nyc 是“感知时区”的 datetime 对象,因为它们分别表示伦敦和纽约的新年开始时刻。

将这两个“感知时区”的 datetime 对象相减得到的 timedelta 显示了 5 小时的时间间隔,即它们的 UTC 时差。

3. timedelta 不考虑夏令时变更:

nyc = zoneinfo.ZoneInfo("America/New_York")

first_time = datetime.datetime.strptime("2024-03-10 1:00am", "%Y-%m-%d %I:%M%p")
first_time = first_time.replace(tzinfo=nyc)

second_time = datetime.datetime.strptime("2024-03-10 3:00am", "%Y-%m-%d %I:%M%p")
second_time = second_time.replace(tzinfo=nyc)

time_difference = second_time - first_time
print("Time difference between the first and second time:")
print(time_difference)

print("Checking the DST status of the first and second time:")
print(f"{first_time.dst() = }")
print(f"{second_time.dst() = }")
Time difference between the first and second time:
2:00:00

Checking the DST status of the first and second time:
first_time.dst() = datetime.timedelta(0)
second_time.dst() = datetime.timedelta(seconds=3600)

这两个时间分别是 2024 年 3 月 3 日纽约的凌晨 1:00 和 3:00。同一天的 2:00 开始进入夏令时,时钟跳到 3:00。两次时间的差值得到的 timedelta 为两小时,尽管由于进入夏令时,实际上仅过去了一小时。

如果需要实际经过的时间,我们可以先转换为 UTC:

first_time_utc = first_time.astimezone(zoneinfo.ZoneInfo("UTC"))
second_time_utc = second_time.astimezone(zoneinfo.ZoneInfo("UTC"))
time_difference_utc = second_time_utc - first_time_utc

print("Actual time difference between the first and second time:")
print(time_difference_utc)
Actual time difference between the first and second time:
1:00:00

在相减这两个 datetime 对象之前转换为 UTC,即可得到它们之间实际经过的时间。

结语

Python 的 timedelta 类为处理时间间隔提供了简明的方法,使我们更容易以各种单位处理时间段。本文介绍了如何创建 timedelta 对象以及如何对其执行算术运算,包括加、减、乘、除。

您可以在以下文章中阅读更多关于日期和时间的内容:

Stephen Gruppetta

作者

Stephen Gruppetta

我在马耳他大学完成了本科阶段的物理与数学学习。随后前往伦敦,并在帝国理工学院取得物理学博士学位,研究用于成像人类视网膜的新型光学技术。现在,我专注于撰写与传播 Python 相关内容,并教授 Python。

主题