I need a Python3 function that rounds down a datetime.datetime
object to the nearest 5 minutes. Yes, this has been discussed in previous SO posts here and here and even here, but I'm having no luck implementing their solutions.
NOTE: I can not use pandas
I want a function, given the below DateTime (%Y%m%d%H%M
) objects, returns the following:
INPUT OUTPUT
202301131600 202301131600
202301131602 202301131600
202301131604 202301131600
202301131605 202301131605
202301131609 202301131605
202301131610 202301131610
Here's my code, using timedelta as a mechanism:
from datetime import datetime
from datetime import timedelta
def roundDownDateTime(dt):
# Arguments:
# dt DateTime object
delta = timedelta(minutes=5)
return dt - (datetime.min - dt) % delta
tmpDate = datetime.now()
# Print the current time and then rounded-down time:
print("\t" tmpDate.strftime('%Y%m%d%H%M') " --> " (roundDownDateTime(tmpDate)).strftime('%Y%m%d%H%M') )
Here's some output when I test the code multiple times:
202301131652 --> 202301131650
202301131700 --> 202301131655
202301131701 --> 202301131657
Ugh, no good! I adapted my function to this:
def roundDownDateTime(dt):
# Arguments:
# dt DateTime object
n = dt - timedelta(minutes=5)
return datetime(year=n.year, month=n.month, day=n.day, hour=n.hour)
But that was even worse:
202301131703 --> 202301131600
202301131707 --> 202301131700
202301131710 --> 202301131700
I am all thumbs when figuring out this basic datetime
arithmetic stuff; can anyone see my error?
CodePudding user response:
Since you can only affect the minutes by rounding down to the nearest 5 minutes, just figure out how many minutes you need to subtract. Set everything else from the original datetime object, and seconds and microseconds to zero:
def roundDownDateTime(dt):
delta_min = dt.minute % 5
return datetime.datetime(dt.year, dt.month, dt.day,
dt.hour, dt.minute - delta_min)
To test:
import datetime
expio = [['202301131600', '202301131600'],
['202301131602', '202301131600'],
['202301131604', '202301131600'],
['202301131605', '202301131605'],
['202301131609', '202301131605'],
['202301131610', '202301131610']]
for i, eo in expio:
o = roundDownDateTime(datetime.datetime.strptime(i, "%Y%m%d%H%M")).strftime("%Y%m%d%H%M")
assert eo == o
asserts all True
CodePudding user response:
You have (datetime.min - dt)
backwards - this results in a negative value which doesn't behave the way you expect with %
. If you swap to (dt - datetime.min)
you get your expected results:
In []:
def roundDownDateTime(dt, delta=timedelta(minutes=5)):
return dt - (dt - datetime.min) % delta
tmpDate = datetime.now()
tmpDate
Out[]:
datetime.datetime(2023, 1, 13, 11, 36, 7, 821196)
In []:
roundDownDateTime(tmpDate)
Out[]:
datetime.datetime(2023, 1, 13, 11, 35)
In []:
roundDownDateTime(tmpDate, timedelta(minutes=10)
Out[]:
datetime.datetime(2023, 1, 13, 11, 30)
CodePudding user response:
I think I would be inclined to obtain the timestamp and round it then convert back to datetime:
def round_datetime(dt, secs):
return datetime.datetime.fromtimestamp(secs * (dt.timestamp() // secs))
You might test with:
import datetime
import time
def round_datetime(dt, secs):
return datetime.datetime.fromtimestamp(secs * (dt.timestamp() // secs))
while True:
now = datetime.datetime.now()
print(now, round_datetime(now, 5 * 60))
time.sleep(1)