我有一个使用strptime()
函数生成的datetime
对象。
>>> tm
datetime.datetime(2010, 6, 10, 3, 56, 23)
我需要做的是将分钟数四舍五入到最接近的十分钟。到目前为止,我一直在使用round()函数来对分钟数进行处理。
min = round(tm.minute, -1)
然而,与上述示例相同,当分钟值大于56时,它会给出一个无效的时间。例如:3:60。有更好的方法吗?datetime
支持这个吗?这将获取存储在tm中的datetime
对象的“floor”,并将其舍入到tm
之前的10分钟标记。
tm = tm - datetime.timedelta(minutes=tm.minute % 10,
seconds=tm.second,
microseconds=tm.microsecond)
如果你想要对最接近的10分钟做经典舍入,可以这样做:
discard = datetime.timedelta(minutes=tm.minute % 10,
seconds=tm.second,
microseconds=tm.microsecond)
tm -= discard
if discard >= datetime.timedelta(minutes=5):
tm += datetime.timedelta(minutes=10)
或者这样:
tm += datetime.timedelta(minutes=5)
tm -= datetime.timedelta(minutes=tm.minute % 10,
seconds=tm.second,
microseconds=tm.microsecond)
通用函数来将日期时间四舍五入到任意秒的时间间隔:
def roundTime(dt=None, roundTo=60):
"""Round a datetime object to any time lapse in seconds
dt : datetime.datetime object, default now.
roundTo : Closest number of seconds to round to, default 1 minute.
Author: Thierry Husson 2012 - Use it as you want but don't blame me.
"""
if dt == None : dt = datetime.datetime.now()
seconds = (dt.replace(tzinfo=None) - dt.min).seconds
rounding = (seconds+roundTo/2) // roundTo * roundTo
return dt + datetime.timedelta(0,rounding-seconds,-dt.microsecond)
1小时舍入和30分钟舍入的样本:
print roundTime(datetime.datetime(2012,12,31,23,44,59,1234),roundTo=60*60)
2013-01-01 00:00:00
print roundTime(datetime.datetime(2012,12,31,23,44,59,1234),roundTo=30*60)
2012-12-31 23:30:00
dt.replace(hour=0, minute=0, second=0)
代替dt.min
。请注意,这样可以得到相同的效果,但是会保留时区信息。 - skoval00roundTime(datetime.datetime(2012,12,31,23,44,59,1234),roundTo=60*60*24*7)
与 roundTime(datetime.datetime(2012,12,30,23,44,59,1234),roundTo=60*60*24*7)
。 - CPBL我使用了Stijn Nevens的代码(感谢Stijn),并有一个小的附加功能要分享:四舍五入、向下取整和向上取整。
更新于2019-03-09 = Spinxz的评论已被纳入,谢谢。
更新于2019-12-27 = Bart的评论已被纳入,谢谢。
已测试日期差为“X小时”、“X分钟”或“X秒”。
import datetime
def round_time(dt=None, date_delta=datetime.timedelta(minutes=1), to='average'):
"""
Round a datetime object to a multiple of a timedelta
dt : datetime.datetime object, default now.
dateDelta : timedelta object, we round to a multiple of this, default 1 minute.
from: https://dev59.com/tnA75IYBdhLWcg3wH1NB
"""
round_to = date_delta.total_seconds()
if dt is None:
dt = datetime.now()
seconds = (dt - dt.min).seconds
if seconds % round_to == 0 and dt.microsecond == 0:
rounding = (seconds + round_to / 2) // round_to * round_to
else:
if to == 'up':
# // is a floor division, not a comment on following line (like in javascript):
rounding = (seconds + dt.microsecond/1000000 + round_to) // round_to * round_to
elif to == 'down':
rounding = seconds // round_to * round_to
else:
rounding = (seconds + round_to / 2) // round_to * round_to
return dt + datetime.timedelta(0, rounding - seconds, - dt.microsecond)
# test data
print(round_time(datetime.datetime(2019,11,1,14,39,00), date_delta=datetime.timedelta(seconds=30), to='up'))
print(round_time(datetime.datetime(2019,11,2,14,39,00,1), date_delta=datetime.timedelta(seconds=30), to='up'))
print(round_time(datetime.datetime(2019,11,3,14,39,00,776980), date_delta=datetime.timedelta(seconds=30), to='up'))
print(round_time(datetime.datetime(2019,11,4,14,39,29,776980), date_delta=datetime.timedelta(seconds=30), to='up'))
print(round_time(datetime.datetime(2018,11,5,14,39,00,776980), date_delta=datetime.timedelta(seconds=30), to='down'))
print(round_time(datetime.datetime(2018,11,6,14,38,59,776980), date_delta=datetime.timedelta(seconds=30), to='down'))
print(round_time(datetime.datetime(2017,11,7,14,39,15), date_delta=datetime.timedelta(seconds=30), to='average'))
print(round_time(datetime.datetime(2017,11,8,14,39,14,999999), date_delta=datetime.timedelta(seconds=30), to='average'))
print(round_time(datetime.datetime(2019,11,9,14,39,14,999999), date_delta=datetime.timedelta(seconds=30), to='up'))
print(round_time(datetime.datetime(2012,12,10,23,44,59,7769),to='average'))
print(round_time(datetime.datetime(2012,12,11,23,44,59,7769),to='up'))
print(round_time(datetime.datetime(2010,12,12,23,44,59,7769),to='down',date_delta=datetime.timedelta(seconds=1)))
print(round_time(datetime.datetime(2011,12,13,23,44,59,7769),to='up',date_delta=datetime.timedelta(seconds=1)))
print(round_time(datetime.datetime(2012,12,14,23,44,59),date_delta=datetime.timedelta(hours=1),to='down'))
print(round_time(datetime.datetime(2012,12,15,23,44,59),date_delta=datetime.timedelta(hours=1),to='up'))
print(round_time(datetime.datetime(2012,12,16,23,44,59),date_delta=datetime.timedelta(hours=1)))
print(round_time(datetime.datetime(2012,12,17,23,00,00),date_delta=datetime.timedelta(hours=1),to='down'))
print(round_time(datetime.datetime(2012,12,18,23,00,00),date_delta=datetime.timedelta(hours=1),to='up'))
print(round_time(datetime.datetime(2012,12,19,23,00,00),date_delta=datetime.timedelta(hours=1)))
date_delta
等于30秒且to='up'
时,2019-11-07 14:39:00.776980
的结果是2019-11-07 14:39:00
。 - Bart我从最佳答案进行了修改,使用了仅限于datetime对象的适应版本,这避免了将时间转换为秒并使调用代码更易读:
def roundTime(dt=None, dateDelta=datetime.timedelta(minutes=1)):
"""Round a datetime object to a multiple of a timedelta
dt : datetime.datetime object, default now.
dateDelta : timedelta object, we round to a multiple of this, default 1 minute.
Author: Thierry Husson 2012 - Use it as you want but don't blame me.
Stijn Nevens 2014 - Changed to use only datetime objects as variables
"""
roundTo = dateDelta.total_seconds()
if dt == None : dt = datetime.datetime.now()
seconds = (dt - dt.min).seconds
# // is a floor division, not a comment on following line:
rounding = (seconds+roundTo/2) // roundTo * roundTo
return dt + datetime.timedelta(0,rounding-seconds,-dt.microsecond)
1小时和15分钟取整的样例:
print roundTime(datetime.datetime(2012,12,31,23,44,59),datetime.timedelta(hour=1))
2013-01-01 00:00:00
print roundTime(datetime.datetime(2012,12,31,23,44,49),datetime.timedelta(minutes=15))
2012-12-31 23:30:00
print roundTime(datetime.datetime(2012,12,20,23,44,49),datetime.timedelta(days=15))
输出结果是 2012-12-20 00:00:00
。相反,使用以下代码:
print roundTime(datetime.datetime(2012,12,21,23,44,49),datetime.timedelta(days=15))
输出结果为 2012-12-21 00:00:00
。 - CPBL这里有一个更简单的通用解决方案,不会出现浮点精度问题,也不需要外部库依赖:
import datetime
def time_mod(time, delta, epoch=None):
if epoch is None:
epoch = datetime.datetime(1970, 1, 1, tzinfo=time.tzinfo)
return (time - epoch) % delta
def time_round(time, delta, epoch=None):
mod = time_mod(time, delta, epoch)
if mod < delta / 2:
return time - mod
return time + (delta - mod)
def time_floor(time, delta, epoch=None):
mod = time_mod(time, delta, epoch)
return time - mod
def time_ceil(time, delta, epoch=None):
mod = time_mod(time, delta, epoch)
if mod:
return time + (delta - mod)
return time
就您的情况而言:
>>> tm = datetime.datetime(2010, 6, 10, 3, 56, 23)
>>> time_round(tm, datetime.timedelta(minutes=10))
datetime.datetime(2010, 6, 10, 4, 0)
>>> time_floor(tm, datetime.timedelta(minutes=10))
datetime.datetime(2010, 6, 10, 3, 50)
>>> time_ceil(tm, datetime.timedelta(minutes=10))
datetime.datetime(2010, 6, 10, 4, 0)
time round to
函数。如何制作“时间 floor to”函数?我的意思是,例如如果时间在00:00和00:10之间,则将其放置在00:00。如果它在00:10和00:20之间,则将其放置在00:10等。 - s.paszkotime_round
函数中,将以if mod < (delta / 2):
开头的三行替换为一行return time - mod
。相应地更新了答案。 - ofotime_ceil
函数存在错误。如果模数为 0
,则应该保留原始时间。就像在数学中 Ceil(1) = 1,但是 Ceil(1.000001) 等于 2。 - s.paszkoPandas有一个日期时间舍入功能,但与Pandas中的大多数功能一样,它需要以Series格式呈现。
>>> ts = pd.Series(pd.date_range(Dt(2019,1,1,1,1),Dt(2019,1,1,1,4),periods=8))
>>> print(ts)
0 2019-01-01 01:01:00.000000000
1 2019-01-01 01:01:25.714285714
2 2019-01-01 01:01:51.428571428
3 2019-01-01 01:02:17.142857142
4 2019-01-01 01:02:42.857142857
5 2019-01-01 01:03:08.571428571
6 2019-01-01 01:03:34.285714285
7 2019-01-01 01:04:00.000000000
dtype: datetime64[ns]
>>> ts.dt.round('1min')
0 2019-01-01 01:01:00
1 2019-01-01 01:01:00
2 2019-01-01 01:02:00
3 2019-01-01 01:02:00
4 2019-01-01 01:03:00
5 2019-01-01 01:03:00
6 2019-01-01 01:04:00
7 2019-01-01 01:04:00
dtype: datetime64[ns]
文档 - 根据需要更改频率字符串。
Timestamp
包含 floor
和 ceil
。 - poulter7我认为这样做可以解决问题,它使用了round函数的一种非常有用的应用。
from typing import Literal
import math
def round_datetime(dt: datetime.datetime, step: datetime.timedelta, d: Literal['no', 'up', 'down'] = 'no'):
step = step.seconds
round_f = {'no': round, 'up': math.ceil, 'down': math.floor}
return datetime.datetime.fromtimestamp(step * round_f[d](dt.timestamp() / step))
date = datetime.datetime(year=2022, month=11, day=16, hour=10, minute=2, second=30, microsecond=424242)#
print('Original:', date)
print('Standard:', round_datetime(date, datetime.timedelta(minutes=5)))
print('Down: ', round_datetime(date, datetime.timedelta(minutes=5), d='down'))
print('Up: ', round_datetime(date, datetime.timedelta(minutes=5), d='up'))
结果:
Original: 2022-11-16 10:02:30.424242
Standard: 2022-11-16 10:05:00
Down: 2022-11-16 10:00:00
Up: 2022-11-16 10:05:00
用于向下舍入分钟时间的通用函数:
from datetime import datetime
def round_minute(date: datetime = None, round_to: int = 1):
"""
round datetime object to minutes
"""
if not date:
date = datetime.now()
date = date.replace(second=0, microsecond=0)
delta = date.minute % round_to
return date.replace(minute=date.minute - delta)
dt.replace
是最直接的四舍五入方法。 - valentinmk如果您不想使用条件语句,可以使用模运算
操作符:
minutes = int(round(tm.minute, -1)) % 60
更新
你是否想要类似这样的东西?
def timeround10(dt):
a, b = divmod(round(dt.minute, -1), 60)
return '%i:%02i' % ((dt.hour + a) % 24, b)
timeround10(datetime.datetime(2010, 1, 1, 0, 56, 0)) # 0:56
# -> 1:00
timeround10(datetime.datetime(2010, 1, 1, 23, 56, 0)) # 23:56
# -> 0:00
如果您想要字符串结果,那么使用timedelta获取日期时间结果会更好-请参考其他回答 ;)
一个简单的方法:
def round_time(dt, round_to_seconds=60):
"""Round a datetime object to any number of seconds
dt: datetime.datetime object
round_to_seconds: closest number of seconds for rounding, Default 1 minute.
"""
rounded_epoch = round(dt.timestamp() / round_to_seconds) * round_to_seconds
rounded_dt = datetime.datetime.fromtimestamp(rounded_epoch).astimezone(dt.tzinfo)
return rounded_dt
floor(...)
的方法。 - Jus