问题:如何舍入日期时间对象的分钟
I have a datetime object produced using
strptime()。
>>> tm
datetime.datetime(2010, 6, 10, 3, 56, 23)
我需要做的是将分钟调整为最接近的第10分钟。到目前为止,我一直在做分钟值并在其上使用round()。
min = round(tm.minute, -1)
但是,与上面的示例一样,当分钟值大于56时,它将给出无效的时间。即:3:60
有什么更好的方法可以做到这一点?是否datetime
支持?
回答 0
这将使datetime
存储在tm 中的对象的“底楼” 四舍五入到之前的10分钟标记tm
。
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)
回答 1
用于舍入日期时间的通用功能,以秒为单位:
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
回答 2
从最好的答案我修改为仅使用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
回答 3
我使用了Stijn Nevens代码(谢谢Stijn),并且有一些共享的附件。向上,向下取整并四舍五入到最接近的值。
更新2019-03-09 =评论并入Spinxz; 谢谢。
更新2019-12-27 =评论Bart纳入; 谢谢。
测试了“ X小时”或“ X分钟”或“ X秒”的date_delta。
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: http://stackoverflow.com/questions/3463930/how-to-round-the-minute-of-a-datetime-object-python
"""
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)))
回答 4
Pandas具有日期时间取整功能,但与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]
文档 -根据需要更改频率字符串。
回答 5
如果您不想使用条件,则可以使用modulo
运算符:
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-参见其他响应;)
回答 6
我正在用这个。它具有使用tz知道的日期时间的优势。
def round_minutes(some_datetime: datetime, step: int):
""" round up to nearest step-minutes """
if step > 60:
raise AttrbuteError("step must be less than 60")
change = timedelta(
minutes= some_datetime.minute % step,
seconds=some_datetime.second,
microseconds=some_datetime.microsecond
)
if change > timedelta():
change -= timedelta(minutes=step)
return some_datetime - change
它的缺点是只能为时间片工作少于一个小时。
回答 7
这是一个更简单的通用解决方案,没有浮点精度问题和外部库依赖性:
import datetime as dt
def time_mod(time, delta, epoch=None):
if epoch is None:
epoch = dt.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)
在您的情况下:
>>> tm
datetime.datetime(2010, 6, 10, 3, 56, 23)
>>> time_round(tm, dt.timedelta(minutes=10))
datetime.datetime(2010, 6, 10, 4, 0)
回答 8
def get_rounded_datetime(self, dt, freq, nearest_type='inf'):
if freq.lower() == '1h':
round_to = 3600
elif freq.lower() == '3h':
round_to = 3 * 3600
elif freq.lower() == '6h':
round_to = 6 * 3600
else:
raise NotImplementedError("Freq %s is not handled yet" % freq)
# // is a floor division, not a comment on following line:
seconds_from_midnight = dt.hour * 3600 + dt.minute * 60 + dt.second
if nearest_type == 'inf':
rounded_sec = int(seconds_from_midnight / round_to) * round_to
elif nearest_type == 'sup':
rounded_sec = (int(seconds_from_midnight / round_to) + 1) * round_to
else:
raise IllegalArgumentException("nearest_type should be 'inf' or 'sup'")
dt_midnight = datetime.datetime(dt.year, dt.month, dt.day)
return dt_midnight + datetime.timedelta(0, rounded_sec)
回答 9
基于Stijn Nevens并针对Django进行了修改,以将当前时间四舍五入到最近的15分钟。
from datetime import date, timedelta, datetime, time
def roundTime(dt=None, dateDelta=timedelta(minutes=1)):
roundTo = dateDelta.total_seconds()
if dt == None : dt = 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 + timedelta(0,rounding-seconds,-dt.microsecond)
dt = roundTime(datetime.now(),timedelta(minutes=15)).strftime('%H:%M:%S')
dt = 11:45:00
如果您需要完整的日期和时间,只需删除 .strftime('%H:%M:%S')
回答 10
当捕获到异常时,不是最好的速度,但是这可以工作。
def _minute10(dt=datetime.utcnow()):
try:
return dt.replace(minute=round(dt.minute, -1))
except ValueError:
return dt.replace(minute=0) + timedelta(hours=1)
时机
%timeit _minute10(datetime(2016, 12, 31, 23, 55))
100000 loops, best of 3: 5.12 µs per loop
%timeit _minute10(datetime(2016, 12, 31, 23, 31))
100000 loops, best of 3: 2.21 µs per loop
回答 11
两行直观的解决方案,用于将datetime
对象舍入到给定的时间单位(此处为秒)t
:
format_str = '%Y-%m-%d %H:%M:%S'
t_rounded = datetime.strptime(datetime.strftime(t, format_str), format_str)
如果您想舍入到另一个单位,只需更改format_str
。
这种方法不能像上述方法那样四舍五入到任意时间,而是一种很好的Pythonic方法,可以四舍五入到给定的小时,分钟或秒。
回答 12
其他解决方案:
def round_time(timestamp=None, lapse=0):
"""
Round a timestamp to a lapse according to specified minutes
Usage:
>>> import datetime, math
>>> round_time(datetime.datetime(2010, 6, 10, 3, 56, 23), 0)
datetime.datetime(2010, 6, 10, 3, 56)
>>> round_time(datetime.datetime(2010, 6, 10, 3, 56, 23), 1)
datetime.datetime(2010, 6, 10, 3, 57)
>>> round_time(datetime.datetime(2010, 6, 10, 3, 56, 23), -1)
datetime.datetime(2010, 6, 10, 3, 55)
>>> round_time(datetime.datetime(2019, 3, 11, 9, 22, 11), 3)
datetime.datetime(2019, 3, 11, 9, 24)
>>> round_time(datetime.datetime(2019, 3, 11, 9, 22, 11), 3*60)
datetime.datetime(2019, 3, 11, 12, 0)
>>> round_time(datetime.datetime(2019, 3, 11, 10, 0, 0), 3)
datetime.datetime(2019, 3, 11, 10, 0)
:param timestamp: Timestamp to round (default: now)
:param lapse: Lapse to round in minutes (default: 0)
"""
t = timestamp or datetime.datetime.now() # type: Union[datetime, Any]
surplus = datetime.timedelta(seconds=t.second, microseconds=t.microsecond)
t -= surplus
try:
mod = t.minute % lapse
except ZeroDivisionError:
return t
if mod: # minutes % lapse != 0
t += datetime.timedelta(minutes=math.ceil(t.minute / lapse) * lapse - t.minute)
elif surplus != datetime.timedelta() or lapse < 0:
t += datetime.timedelta(minutes=(t.minute / lapse + 1) * lapse - t.minute)
return t
希望这可以帮助!
回答 13
我知道的最短方法
min = tm.minute // 10 * 10
回答 14
那些看起来太复杂了
def round_down_to():
num = int(datetime.utcnow().replace(second=0, microsecond=0).minute)
return num - (num%10)
回答 15
一种简单的方法:
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
回答 16
是的,如果您的数据属于pandas系列中的DateTime列,则可以使用内置的pandas.Series.dt.round函数将其向上舍入。请参阅pandas.Series.dt.round上的文档。在四舍五入到10分钟的情况下,它将是Series.dt.round(’10min’)或Series.dt.round(’600s’),如下所示:
pandas.Series(tm).dt.round('10min')
编辑以添加示例代码:
import datetime
import pandas
tm = datetime.datetime(2010, 6, 10, 3, 56, 23)
tm_rounded = pandas.Series(tm).dt.round('10min')
print(tm_rounded)
>>> 0 2010-06-10 04:00:00
dtype: datetime64[ns]