How to round the minute of a datetime object
Solution 1
This will get the 'floor' of a datetime
object stored in tm rounded to the 10 minute mark before tm
.
tm = tm - datetime.timedelta(minutes=tm.minute % 10,
seconds=tm.second,
microseconds=tm.microsecond)
If you want classic rounding to the nearest 10 minute mark, do this:
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)
or this:
tm += datetime.timedelta(minutes=5)
tm -= datetime.timedelta(minutes=tm.minute % 10,
seconds=tm.second,
microseconds=tm.microsecond)
Solution 2
General function to round a datetime at any time lapse in seconds:
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)
Samples with 1 hour rounding & 30 minutes rounding:
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
Solution 3
I used Stijn Nevens code (thank you Stijn) and have a little add-on to share. Rounding up, down and rounding to nearest.
update 2019-03-09 = comment Spinxz incorporated; thank you.
update 2019-12-27 = comment Bart incorporated; thank you.
Tested for date_delta of "X hours" or "X minutes" or "X seconds".
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)))
Solution 4
From the best answer I modified to an adapted version using only datetime objects, this avoids having to do the conversion to seconds and makes the calling code more readable:
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)
Samples with 1 hour rounding & 15 minutes rounding:
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
Solution 5
Pandas has a datetime round feature, but as with most things in Pandas it needs to be in Series format.
>>> 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]
Docs - Change the frequency string as needed.
Related videos on Youtube
Lucas Manco
Updated on July 08, 2022Comments
-
Lucas Manco almost 2 years
I have a
datetime
object produced usingstrptime()
.>>> tm datetime.datetime(2010, 6, 10, 3, 56, 23)
What I need to do is round the minute to the closest 10th minute. What I have been doing up to this point was taking the minute value and using round() on it.
min = round(tm.minute, -1)
However, as with the above example, it gives an invalid time when the minute value is greater than 56. i.e.: 3:60
What is a better way to do this? Does
datetime
support this?-
Jus almost 3 yearsToday, timestamp has a method
floor(...)
.
-
-
Lucas Manco over 13 yearsAh but then the problem here is that the hour must increase as well
-
Omnifarious over 13 years@Lucas Manco - My solution also works fine and I think makes more sense.
-
Max over 7 yearsThis helped me. I want to add that if using it in PySpark, to parse the date time as a string rather than a date time object.
-
skoval00 over 7 yearsUnfortunately this does not work with tz-aware datetime. One should use
dt.replace(hour=0, minute=0, second=0)
instead ofdt.min
. -
Le Droid over 7 years@skoval00 + druska Edited following your advices to support tz-aware datetime. Thanks!
-
Michel Mesquita over 7 yearsThanks @skoval00 - it took me a while to figure out why the function was not working with my data
-
CPBL over 7 yearsThis does not work at all for me for long periods. e.g.
roundTime(datetime.datetime(2012,12,31,23,44,59,1234),roundTo=60*60*24*7)
vsroundTime(datetime.datetime(2012,12,30,23,44,59,1234),roundTo=60*60*24*7)
-
CPBL over 7 yearsSee this to understand the problem:
datetime.timedelta(100,1,2,3).seconds == 1
-
CPBL over 7 yearsAlso no good:
print roundTime(datetime.datetime(2012,12,20,23,44,49),datetime.timedelta(days=15))
2012-12-20 00:00:00
whileprint roundTime(datetime.datetime(2012,12,21,23,44,49),datetime.timedelta(days=15))
2012-12-21 00:00:00
-
Le Droid over 7 years@CPBL This function is to answer "How to round the minute" and is named "roundTime", not roundWeek or roundMonth. To round to closest monday you could replace the line with
tdelta = (dt.replace(tzinfo=None) - dt.min); seconds = tdelta.seconds + tdelta.days * 86400
but I will not edit this as it doesn't make sense to roundTime something bigger than a day as the understanding of it would be arbitrary. Like, what do you expect if you round to 3 days? -
CPBL over 7 yearsFollow-up to above: Just pointing out that it does not work for arbitrary time deltas, e.g. those over 1 day. This question is about rounding minutes, so that's an appropriate restriction, but it could be clearer in the way the code is written.
-
CPBL over 7 years@LeDroid : Thanks; quite right. But your code says "any time laps[e] in seconds". Not sure how 3 days is harder to imagine than, say, 7: I guess in either case, you need to pick an arbitrary starting day. Anyway, I agree that should be a new question, but your code could clarify (or enforce) its limitation.
-
spinxz over 6 yearsThe 'up' rounding is maybe not doing what most people expect. You would round up to the next date_delta even if dt would not need rounding: e.g. 15:30:00.000 with round_to = 60 would become 15:31:00.000
-
Samuel Dauzon over 5 yearsDont' forget to see @Omnifarious answer ! He explain how to get the floor of datetime.
-
poulter7 almost 5 yearsFor reference,
Timestamp
containsfloor
andceil
as well -
Bart over 4 yearsThe
up
rounding is anyhow inaccurate with this function;2019-11-07 14:39:00.776980
withdate_delta
equal to e.g. 30 sec andto='up'
results in2019-11-07 14:39:00
. -
Rahul Bharadwaj almost 4 yearsThanks a lot!! Although
up
rounding might not be a common use case, it is needed when you are dealing with applications which starts at minute boundary -
Georgy almost 4 yearsI'm not sure that this answer adds anything new or useful. There was already an answer that explained the same thing: stackoverflow.com/a/56010357/7851470
-
Nguyen Bryan almost 4 yearsyes, thank you for pointing these out to me. It's my mistake for not including samples code into my response, and also not checking out all other people's responses. I will try to improve on this aspect.
-
cona about 3 years@poulter7 The floor functions though only works on a single value, not a Datetime index
-
interestedparty333 about 3 yearsthis may not account for > 60 minutes
-
Cairan Van Rooyen almost 3 yearsGreat answer, with options for rounding up and down, very useful for me!
-
s.paszko over 2 yearsThis is
time round to
function. How to makeTime floor to
function? I mean for example if time is between 00:00 and 00:10 then it's floored to 00:00. If it's between 00:10 and 00:20 then it's floored to 00:10 etc. -
ofo over 2 years@s.paszko replace three lines starting from
if mod < (delta / 2):
with a single linereturn time - mod
in thetime_round
function. Updated the answer accordingly. -
s.paszko over 2 yearsBig thx for updated answer!
-
s.paszko over 2 yearsThere is an error in
time_ceil
. If mod iszero
then it should leave original time. Like in math Ceil(1) = 1, but Ceil(1.000001) is 2. -
ofo over 2 years@s.paszko Corrected it