跳转到主要内容

类似于datetime.timedelta,用于日期算术。

项目描述

datedelta.datedeltadatetime.timedelta 用于日期算术。

>>> import datetime
>>> import datedelta

>>> datetime.date(2025, 4, 22) + 2 * datedelta.WEEK
datetime.date(2025, 5, 6)

>>> datetime.date(2025, 4, 22) + 3 * datedelta.MONTH
datetime.date(2025, 7, 22)

它考虑了格里高利日历的奇异性。

>>> datetime.date(2024, 2, 29) + datedelta.YEAR
datetime.date(2025, 3, 1)

>>> datetime.date(2024, 2, 29) + 4 * datedelta.YEAR
datetime.date(2028, 2, 29)

它方便计算年度、月度或周订阅周期。

>>> start_date = datetime.date(2024, 1, 30)
>>> for n in range(12):
...     print(repr(start_date + n * datedelta.MONTH))
datetime.date(2024, 1, 30)
datetime.date(2024, 3, 1)
datetime.date(2024, 3, 30)
datetime.date(2024, 4, 30)
datetime.date(2024, 5, 30)
datetime.date(2024, 6, 30)
datetime.date(2024, 7, 30)
datetime.date(2024, 8, 30)
datetime.date(2024, 9, 30)
datetime.date(2024, 10, 30)
datetime.date(2024, 11, 30)
datetime.date(2024, 12, 30)

>>> start_date = datetime.date(2024, 1, 31)
>>> for n in range(12):
...     print(repr(start_date + n * datedelta.MONTH))
datetime.date(2024, 1, 31)
datetime.date(2024, 3, 1)
datetime.date(2024, 3, 31)
datetime.date(2024, 5, 1)
datetime.date(2024, 5, 31)
datetime.date(2024, 7, 1)
datetime.date(2024, 7, 31)
datetime.date(2024, 8, 31)
datetime.date(2024, 10, 1)
datetime.date(2024, 10, 31)
datetime.date(2024, 12, 1)
datetime.date(2024, 12, 31)

它保证它支持的算术操作结果一致。

行为

格里高利日历中有两个日期算术陷阱

  1. 闰年。当将年份加到2月29日时,如果结果是平年,则会出问题。

  2. 月份中天数不同。当将月份加到29日、30日或31日时,如果结果是那个月不存在的那一天,则会出问题。

在这两种情况下,datedelta将结果更改为下一个月的第一天。

如果周期以(起始日期包含,结束日期排除)表示——如果你喜欢数学符号,那就是[start date, end date),这种方法给出的结果是一致的。这种周期表示类似于0索引,这是Python使用的约定。

例如

  • 如果某人在2020-02-29(包含)开始订阅一年,则结束日期必须是2021-03-01(排除)。如果它是2020-02-28(排除),则那天将从订阅周期中缺失。

  • 如果某人在2020-03-31(包含)开始订阅三个月,则结束日期必须是2020-07-01(排除)。如果它是2020-06-30(排除),则那天将从订阅周期中缺失。

操作总是按年、月、日的顺序执行。这种顺序通常提供预期的行为。它还可以最小化精度损失。

安装

pip install datedelta

使用

最常见的操作是将 datedelta 添加到日期,以及从日期中减去 datedelta

基本间隔

YEARMONTHWEEKDAY 常量支持使用少量代码进行常见计算。

>>> import datetime
>>> import datedelta

>>> datetime.date(2022, 1, 1) + datedelta.YEAR
datetime.date(2023, 1, 1)

>>> datetime.date(2023, 1, 1) - datedelta.YEAR
datetime.date(2022, 1, 1)

>>> datetime.date(2024, 2, 29) + datedelta.YEAR
datetime.date(2025, 3, 1)

>>> datetime.date(2025, 3, 1) - datedelta.YEAR
datetime.date(2024, 3, 1)

>>> datetime.date(2022, 1, 1) + datedelta.MONTH
datetime.date(2022, 2, 1)

>>> datetime.date(2022, 2, 1) - datedelta.MONTH
datetime.date(2022, 1, 1)

>>> datetime.date(2022, 1, 31) + datedelta.MONTH
datetime.date(2022, 3, 1)

>>> datetime.date(2022, 3, 1) - datedelta.MONTH
datetime.date(2022, 2, 1)

>>> datetime.date(2022, 1, 1) + datedelta.WEEK
datetime.date(2022, 1, 8)

>>> datetime.date(2022, 1, 1) - datedelta.WEEK
datetime.date(2021, 12, 25)

>>> datetime.date(2022, 1, 1) + datedelta.DAY
datetime.date(2022, 1, 2)

>>> datetime.date(2022, 1, 1) - datedelta.DAY
datetime.date(2021, 12, 31)

datedelta.DAY 的行为与 datetime.timedelta(1) 完全相同。它仅提供以保持一致性。

任意间隔

datedelta 对象提供对任意计算的支持。

>>> import datetime
>>> import datedelta

>>> datetime.date(2022, 3, 23) + datedelta.datedelta(years=1, months=1, days=-1)
datetime.date(2023, 4, 22)

>>> datetime.date(2022, 3, 23) - datedelta.datedelta(years=-1, months=-1, days=1)
datetime.date(2023, 4, 22)

>>> datetime.date(2024, 2, 29) + datedelta.datedelta(years=2)
datetime.date(2026, 3, 1)

>>> datetime.date(2024, 2, 29) - datedelta.datedelta(years=2)
datetime.date(2022, 3, 1)

>>> datetime.date(2024, 2, 29) + datedelta.datedelta(years=2, days=-1)
datetime.date(2026, 2, 28)

>>> datetime.date(2024, 2, 29) - datedelta.datedelta(years=2, days=1)
datetime.date(2022, 2, 28)

>>> datetime.date(2024, 2, 29) + datedelta.datedelta(years=2, months=6)
datetime.date(2026, 9, 1)

>>> datetime.date(2024, 2, 29) - datedelta.datedelta(years=2, months=-6)
datetime.date(2022, 9, 1)

>>> datetime.date(2024, 2, 29) + datedelta.datedelta(years=4)
datetime.date(2028, 2, 29)

>>> datetime.date(2024, 2, 29) - datedelta.datedelta(years=4)
datetime.date(2020, 2, 29)

>>> datetime.date(2024, 2, 29) + datedelta.datedelta(years=4, days=1)
datetime.date(2028, 3, 1)

>>> datetime.date(2024, 2, 29) - datedelta.datedelta(years=4, days=-1)
datetime.date(2020, 3, 1)

>>> datetime.date(2024, 2, 29) + datedelta.datedelta(years=4, months=6)
datetime.date(2028, 8, 29)

>>> datetime.date(2024, 2, 29) - datedelta.datedelta(years=4, months=-6)
datetime.date(2020, 8, 29)

这些结果与上述“行为”中解释的数学上一致。

其他操作

datedelta 实例可以与整数相加、相减和相乘。但是,在加法和减法方面有一些限制。

将给定的 datedelta 添加到日期并从中减去,并不总是返回原始日期。为了防止由此行为引起的错误,当添加或减去两个 datedelta 的结果不明确时,会引发一个 ValueError

>>> import datedelta

>>> datedelta.YEAR + datedelta.YEAR
datedelta.datedelta(years=2)

>>> 3 * datedelta.YEAR
datedelta.datedelta(years=3)

>>> datedelta.YEAR - datedelta.DAY
datedelta.datedelta(years=1, days=-1)

>>> datedelta.YEAR - datedelta.YEAR
Traceback (most recent call last):
    ...
ValueError: cannot subtract datedeltas with same signs

>>> datedelta.datedelta(months=6) + datedelta.datedelta(months=-3)
Traceback (most recent call last):
    ...
ValueError: cannot add datedeltas with opposite signs

限制

涉及 datedelta 的加法在一般情况下既不具有结合性也不具有交换性。

以下两个例子表明,添加 datedelta 然后减去它并不总是返回原始值

>>> import datetime
>>> import datedelta

>>> datetime.date(2024, 2, 29) + datedelta.datedelta(years=1)
datetime.date(2025, 3, 1)

>>> datetime.date(2025, 3, 1) - datedelta.datedelta(years=1)
datetime.date(2024, 3, 1)

>>> datetime.date(2024, 1, 31) + datedelta.datedelta(months=1)
datetime.date(2024, 3, 1)

>>> datetime.date(2024, 3, 1) - datedelta.datedelta(months=1)
datetime.date(2024, 2, 1)

以下两个例子表明,添加两个 datedelta 的结果取决于操作顺序

>>> import datetime
>>> import datedelta

>>> datetime.date(2024, 2, 29) + datedelta.datedelta(months=6) + datedelta.datedelta(years=1)
datetime.date(2025, 8, 29)

>>> datetime.date(2024, 2, 29) + datedelta.datedelta(years=1) + datedelta.datedelta(months=6)
datetime.date(2025, 9, 1)

>>> datetime.date(2024, 1, 31) + datedelta.datedelta(months=2) + datedelta.datedelta(months=5)
datetime.date(2024, 8, 31)

>>> datetime.date(2024, 1, 31) + datedelta.datedelta(months=5) + datedelta.datedelta(months=2)
datetime.date(2024, 9, 1)

为了避免问题,您应该始终从相同的参考日期开始,并添加单个 datedelta。不要链式添加或减去。

为了最小化错误结果的风险,datedelta 只实现了具有明确语义的操作

  • datedelta 添加到日期

  • 从日期中减去 datedelta

  • 当组成部分具有相同符号时,将 datedelta 添加到 datedelta

  • 当组成部分具有相反符号时,从 datedelta 中减去 datedelta

(PEP 20 说:“面对歧义,拒绝猜测的诱惑。”)

替代方案

datedelta.datedelta 比标准的 datetime.timedelta 更智能,因为它了解除了天以外还有年和月。

pendulum.Durationdateutil.relativedelta.relativedelta 相比,datedelta.datedelta 有一些优点

  • 它以一种数学上一致的方式处理非现有结果:它调整到下一个月的第一天,而 pendulum 和 dateutil 调整到当前月的最后一天。

  • 它提供了一个旨在防止编程错误的 API:它要求关键字参数,拒绝表达不正确业务逻辑的操作,并省略了 dateutil 中的易出错的特性,如“替换”行为或对闰日的显式控制。

  • 它具有非常小的内存占用,因为它提供了 pendulum 或 dateutil 中非常小的功能子集。这使得它在您对标准库的 datetime 模块感到满意的情况下成为一个不错的选择。

变更日志

1.3

  • 添加 WEEK 常量。

1.2

  • 优化哈希和序列化。

1.1

  • 添加 YEARMONTHDAY 常量。

1.0

  • 初始稳定版本。

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。

源代码分发

datedelta-1.4.tar.gz (8.0 kB 查看哈希值)

上传时间 源代码

构建分发

datedelta-1.4-py3-none-any.whl (6.4 kB 查看哈希值)

上传时间 Python 3

由以下支持