在 Python 中向 datetime.time 添加 N 秒的标准方法是什么?
- 2025-01-09 08:46:00
- admin 原创
- 133
问题描述:
在 Python 中,给定一个datetime.time
值,是否有一种标准方法可以向该值添加整数秒数,例如11:34:59
+ 3 = 11:35:02
?
这些显而易见的想法不起作用:
>>> datetime.time(11, 34, 59) + 3
TypeError: unsupported operand type(s) for +: 'datetime.time' and 'int'
>>> datetime.time(11, 34, 59) + datetime.timedelta(0, 3)
TypeError: unsupported operand type(s) for +: 'datetime.time' and 'datetime.timedelta'
>>> datetime.time(11, 34, 59) + datetime.time(0, 0, 3)
TypeError: unsupported operand type(s) for +: 'datetime.time' and 'datetime.time'
最后我写了如下函数:
def add_secs_to_time(timeval, secs_to_add):
secs = timeval.hour * 3600 + timeval.minute * 60 + timeval.second
secs += secs_to_add
return datetime.time(secs // 3600, (secs % 3600) // 60, secs % 60)
我禁不住想到我错过了一种更简单的方法来做到这一点。
有关的
python 时间 + timedelta 等效项
解决方案 1:
您可以使用完整datetime
变量timedelta
,并通过提供一个虚拟日期然后使用它time
来获取时间值。
例如:
import datetime
a = datetime.datetime(100,1,1,11,34,59)
b = a + datetime.timedelta(0,3) # days, seconds, then other fields.
print(a.time())
print(b.time())
得出两个值,相隔三秒:
11:34:59
11:35:02
你也可以选择更易读的
b = a + datetime.timedelta(seconds=3)
如果你愿意的话。
如果您需要实现此功能,可以研究addSecs
以下方法:
import datetime
def addSecs(tm, secs):
fulldate = datetime.datetime(100, 1, 1, tm.hour, tm.minute, tm.second)
fulldate = fulldate + datetime.timedelta(seconds=secs)
return fulldate.time()
a = datetime.datetime.now().time()
b = addSecs(a, 300)
print(a)
print(b)
输出如下:
09:11:55.775695
09:16:55
解决方案 2:
正如其他人所说,您可以始终使用完整的日期时间对象:
from datetime import datetime, date, time, timedelta
sometime = time(8,00) # 8am
later = (datetime.combine(date.today(), sometime) + timedelta(seconds=3)).time()
但是,我认为有必要解释一下为什么需要完整的日期时间对象。考虑一下如果我在晚上 11 点加上 2 个小时会发生什么。正确的行为是什么?一个例外,因为你不能有大于晚上 11:59 的时间?它应该绕回来吗?
不同的程序员会有不同的期望,因此无论他们选择哪种结果都会让很多人感到惊讶。更糟糕的是,程序员编写的代码在最初测试时运行良好,然后后来由于做了一些意想不到的事情而导致代码崩溃。这非常糟糕,这就是为什么不允许将 timedelta 对象添加到 time 对象的原因。
解决方案 3:
一件小事可能会增加清晰度以覆盖秒的默认值
>>> b = a + datetime.timedelta(seconds=3000)
>>> b
datetime.datetime(1, 1, 1, 12, 24, 59)
解决方案 4:
您不能简单地添加数字,datetime
因为不清楚使用的是什么单位:秒、小时、周......
有一个timedelta
用于操作日期和时间的类。datetime
减法datetime
得到timedelta
,datetime
加法timedelta
得到datetime
,两个datetime
对象可以相加,但timedelta
不能相加。
创建timedelta
对象并指定要添加的秒数,然后将其添加到datetime
对象中:
>>> from datetime import datetime, timedelta
>>> t = datetime.now() + timedelta(seconds=3000)
>>> print(t)
datetime.datetime(2018, 1, 17, 21, 47, 13, 90244)
C++ 中也有同样的概念:std::chrono::duration
。
解决方案 5:
感谢@Pax Diablo、@bvmou 和@Arachnid 建议始终使用完整日期时间。如果我必须从外部源接受 datetime.time 对象,那么这似乎是一个替代add_secs_to_time()
函数:
def add_secs_to_time(timeval, secs_to_add):
dummy_date = datetime.date(1, 1, 1)
full_datetime = datetime.datetime.combine(dummy_date, timeval)
added_datetime = full_datetime + datetime.timedelta(seconds=secs_to_add)
return added_datetime.time()
这段详细代码可以压缩为一行:
(datetime.datetime.combine(datetime.date(1, 1, 1), timeval) + datetime.timedelta(seconds=secs_to_add)).time()
但无论如何,我认为为了代码清晰起见,我还是想把它包装在一个函数中。
解决方案 6:
如果有必要向您的项目添加另一个文件/依赖项,我刚刚编写了一个很小的类,它扩展了datetime.time
算术能力。当您超过午夜时,它会绕零旋转。现在,“从现在起 24 小时后是几点”有很多极端情况,包括夏令时、闰秒、历史时区变化等等。但有时您确实需要简单的情况,这就是它能做到的。
你的例子可以这样写:
>>> import datetime
>>> import nptime
>>> nptime.nptime(11, 34, 59) + datetime.timedelta(0, 3)
nptime(11, 35, 2)
nptime
继承自datetime.time
,因此任何这些方法也应该可以使用。
它可以从 PyPi 获得nptime
(“非迂腐时间”),也可以在 GitHub 上获得:https ://github.com/tgs/nptime
解决方案 7:
在现实环境中,单独使用从来都不是一个好主意time
,总是使用datetime
,甚至更好utc
,以避免诸如隔夜、夏令时、用户和服务器之间的不同时区等冲突。
因此我推荐这种方法:
import datetime as dt
_now = dt.datetime.now() # or dt.datetime.now(dt.timezone.utc)
_in_5_sec = _now + dt.timedelta(seconds=5)
# get '14:39:57':
_in_5_sec.strftime('%H:%M:%S')
解决方案 8:
arrow
为了完整起见,这里是使用(Python 的更好的日期和时间)的方法:
sometime = arrow.now()
abitlater = sometime.shift(seconds=3)
解决方案 9:
如果您还没有 timedelta 对象,另一种可能性是仅使用旧对象的属性初始化一个新的时间对象并在需要时添加值:
new_time:time = time(
hour=curr_time.hour + n_hours,
minute=curr_time.minute + n_minutes,
seconds=curr_time.second + n_seconds
)
诚然,这只有在你对值做出一些假设时才有效,因为这里不处理溢出。但我认为记住这一点是值得的,因为它可以节省一两行
解决方案 10:
尝试将 a 添加datetime.datetime
到 a datetime.timedelta
。如果您只想要时间部分,可以调用time()
结果datetime.datetime
对象上的方法来获取它。
解决方案 11:
老问题了,但我想我会加入一个处理时区的函数。关键部分是将datetime.time
对象的tzinfo
属性传递给组合,然后在生成的虚拟日期时间上使用timetz()
而不是。这个答案部分受到此处其他答案的启发。time()
def add_timedelta_to_time(t, td):
"""Add a timedelta object to a time object using a dummy datetime.
:param t: datetime.time object.
:param td: datetime.timedelta object.
:returns: datetime.time object, representing the result of t + td.
NOTE: Using a gigantic td may result in an overflow. You've been
warned.
"""
# Create a dummy date object.
dummy_date = date(year=100, month=1, day=1)
# Combine the dummy date with the given time.
dummy_datetime = datetime.combine(date=dummy_date, time=t, tzinfo=t.tzinfo)
# Add the timedelta to the dummy datetime.
new_datetime = dummy_datetime + td
# Return the resulting time, including timezone information.
return new_datetime.timetz()
这是一个非常简单的测试用例类(使用内置的unittest
):
import unittest
from datetime import datetime, timezone, timedelta, time
class AddTimedeltaToTimeTestCase(unittest.TestCase):
"""Test add_timedelta_to_time."""
def test_wraps(self):
t = time(hour=23, minute=59)
td = timedelta(minutes=2)
t_expected = time(hour=0, minute=1)
t_actual = add_timedelta_to_time(t=t, td=td)
self.assertEqual(t_expected, t_actual)
def test_tz(self):
t = time(hour=4, minute=16, tzinfo=timezone.utc)
td = timedelta(hours=10, minutes=4)
t_expected = time(hour=14, minute=20, tzinfo=timezone.utc)
t_actual = add_timedelta_to_time(t=t, td=td)
self.assertEqual(t_expected, t_actual)
if __name__ == '__main__':
unittest.main()
扫码咨询,免费领取项目管理大礼包!