'Rounding up to nearest 30 minutes in python
I have the following code below.
I would like to roundup TIME to the nearest 30 minutes in the hour. For example: 12:00PM or 12:30PM and so on.
EASTERN_NOW = timezone.localtime(timezone.now() + timedelta(minutes=30))
TIME = datetime.time(EASTERN_NOW.time().hour, EASTERN_NOW.time().minute).strftime(
VALID_TIME_FORMATS[2])
Thanks in advance
Solution 1:[1]
To round up to the nearest 30 minutes:
#!/usr/bin/env python3
from datetime import datetime, timedelta
def ceil_dt(dt, delta):
return dt + (datetime.min - dt) % delta
now = datetime.now()
print(now)
print(ceil_dt(now, timedelta(minutes=30)))
The formula is suggested by @Mark Dickinson (for a different question).
Output
2015-09-22 19:08:34.839915
2015-09-22 19:30:00
Note: if the input is timezone-aware datetime object such as EASTERN_NOW in your case then you should call timezone.make_aware(rounded_dt.replace(tzinfo=None)) if you want to preserve the rounded local time and to attach the correct tzinfo, otherwise you may get wrong timezone info if the rounding crosses DST boundaries. Or to avoid failing for ambiguous local time, call .localize() manually:
localize = getattr(rounded_dt.tzinfo, 'localize', None)
if localize:
rounded_dt = localize(rounded_dt.replace(tzinfo=None),
is_dst=bool(rounded_dt.dst()))
Solution 2:[2]
to round forward you can use :
#!/usr/bin/env python3
from datetime import datetime, timedelta
def ceil_dt(dt, delta):
return dt + (datetime.min - dt) % delta
now = datetime.now()
print(now)
print(ceil_dt(now, timedelta(minutes=30)))
To round back to the nearest 30th minute
def rounded_to_the_last_30th_minute_epoch():
now = datetime.now()
rounded = now - (now - datetime.min) % timedelta(minutes=30)
return rounded
Solution 3:[3]
You can divide your minutes by 30, round that and multiply by 30 again to get either 0, 30 or 60 minutes:
date = datetime.datetime(2015, 9, 22, 12, 35)
approx = round(date.minute/30.0) * 30
date = date.replace(minute=0)
date += datetime.timedelta(seconds=approx * 60)
time = date.time()
print(time.strftime('%H:%M'))
# prints '13:30'
I'm using a datetime object because timedelta doesn't work with time objects. In the end you can obtain the time using date.time().
Solution 4:[4]
you can just take datetime input apart and ajust time
def ajustTime():
from datetime import datetime
mytime= datetime.now().strftime("%Y-%m-%d %H-%M")
m = mytime.split()
hours, mints = m[1].split('-')
if 15 <= int(mints) <= 45:
mints = ':30'
elif int(mints) < 15:
mints = ':00'
elif int(mints) > 45:
mints = ':00'
h = int(hours) + 1
hours = str(h)
print(m[0] + " " + hours + mints)
ajustTime()
output
2015-09-22 15:42:03.587633
2015-09-22 15:30
2015-09-22 15:46:01.956860
2015-09-22 16:00
Solution 5:[5]
>>> from dateutil.rrule import rrule, MINUTELY
>>> import datetime
>>> import bisect
>>> times = list(rrule(MINUTELY,interval=30,dtstart=datetime.date.today(),count=
48))
>>> print times[bisect.bisect(times,datetime.datetime.now())]
2015-09-22 11:00:00
>>>
Note that this solution uses the 3rd party dateutil library that can be installed with pip install dateutil... Of course, you could solve it without it... but it's easier with it.
Solution 6:[6]
To round down:
- extract the minute from the time that you want to round
- using floor division, find the whole number intervals there are in the current hour (from the floor division of the current minute and the time interval, i.e. the intervals that you want to round down to).
- multiply the time interval by that whole number, which gives you the rounded down minutes
- use .replace() to change the minutes to those rounded down minutes (and seconds and milliseconds to zero).
Or, in words, using the number of whole intervals (x) there are in the current minutes, replace the minutes by that number of intervals (x * interval):
#!/usr/bin/env python3
def floor_dt(dt, interval):
replace = (dt.minute // interval)*interval
return dt.replace(minute = replace, second=0, microsecond=0)
print(datetime.now())
# datetime.datetime(2019, 5, 30, 22, 25, 31, 115901)
print(floor_dt(datetime.now(),30))
# datetime.datetime(2019, 5, 30, 22, 0)
print(datetime.now())
# datetime.datetime(2019, 5, 30, 22, 26, 29, 469555)
print(floor_dt(datetime.now(),10))
# datetime.datetime(2019, 5, 30, 22, 20)
Solution 7:[7]
To round down and up you can use these two functions. You don't need to calculate seconds or use math module.
from datetime import timedelta, datetime
def ceil_date(date, **kwargs):
secs = timedelta(**kwargs).total_seconds()
return datetime.fromtimestamp(date.timestamp() + secs - date.timestamp() % secs)
def floor_date(date, **kwargs):
secs = timedelta(**kwargs).total_seconds()
return datetime.fromtimestamp(date.timestamp() - date.timestamp() % secs)
Usage is similar to timedelta because kwargs is passed
now = datetime.now() #15:12:12
ceil_date(now, minutes=5) #15:15:00
floor_date(now, minutes=5) #15:10:00
Solution 8:[8]
I have written this function that rounds to any unit from fraction of seconds to one day:
from datetime import datetime
from datetime import timedelta
def round_time(dt: datetime, unit=timedelta(seconds=1)):
seconds = (dt - datetime.min).total_seconds()
unit_seconds = unit.total_seconds()
half_over = seconds + unit_seconds / 2
rounded_seconds = half_over - half_over % unit_seconds
return datetime.min + timedelta(seconds=rounded_seconds)
Outputs
dt
# datetime.datetime(2022, 2, 3, 17, 44, 56, 659595)
round_time(dt)
# datetime.datetime(2022, 2, 3, 17, 44, 57)
round_time(dt, timedelta(seconds=5))
# datetime.datetime(2022, 2, 3, 17, 44, 55)
round_time(dt, timedelta(seconds=0.25))
# datetime.datetime(2022, 2, 3, 17, 44, 56, 750000)
round_time(dt, timedelta(minutes=1))
# datetime.datetime(2022, 2, 3, 17, 45)
round_time(dt, timedelta(minutes=30))
# datetime.datetime(2022, 2, 3, 17, 30)
round_time(dt, timedelta(hours=1))
# datetime.datetime(2022, 2, 3, 18, 0)
round_time(dt, timedelta(hours=12))
# datetime.datetime(2022, 2, 3, 12, 0)
round_time(dt, timedelta(days=1))
# datetime.datetime(2022, 2, 4, 0, 0)
Solution 9:[9]
this should work too, not sure about time zones though
rounded=time.gmtime(30*60*(round(time.time()/(30*60))))
Solution 10:[10]
Thanks for all the input guys. I solved this with my own approach.
min_time = timezone.localtime(timezone.now())
min_time_est = min_time.minute
if min_time_est > 30:
add_mins = 60 - min_time_est
else:
add_mins = 30 - min_time_est
EASTERN_NOW = timezone.localtime(timezone.now() + timedelta(minutes=add_mins))
TIME = datetime.time(EASTERN_NOW.time().hour, EASTERN_NOW.time().minute).strftime(
VALID_TIME_FORMATS[2])
In case anyone else has a similar problem. The about 'TIME' outputs every 30 mins e.g '1:00PM' or '1:30PM'.
Solution 11:[11]
Round up to nearest amount specified in minutes via round_mins without using third-party libraries and just a few lines of code.
import datetime
round_mins = 30
now = datetime.datetime.now()
mins = now.minute - (now.minute % round_mins)
print datetime.datetime(now.year, now.month, now.day, now.hour, mins) + datetime.timedelta(minutes=round_mins)
Solution 12:[12]
The function below can round any time string to hours, minutes, seconds by setting parmeter 'unit', default is the smallest format of ts. Parameter 'ts' is the time string you want to round, the fuction accept multiple time formats. With parmeter 'rnd' you can set the number you want to round at, default is 1. With parmeter 'frm' you can set the format of the output string, as default the function takes the format of ts. Just play with the function and you will see how easy it will be. The function even convert time strings between 12 and 24 hours format and vice versa. All this with a minimum on import.
def toNearestTime(ts, unit=None, rnd=1, frm=None):
''' round to the Nearest Time
param ts = auto recognize the most time patterns : not date-time
param unit = specify unit wich must be rounded 'sec' or 'min' or 'hour', default is the smallest unit of ts :
param rnd = to which number you will round, the default is 1 :
param frm = the output (return) format of the time string you want, as default the function take the ts format'''
from time import strftime, gmtime, strptime
ustp = ["AM", 'PM']
if any(e in ts for e in ustp):
time12_pat = {'%I:%M%p': 'm', '%I:%M %p': 'm', '%I:%M:%S%p': 'se', '%I:%M:%S %p': 'se', '%I%M%p': 'm', '%I%M %p': 'm', '%I%M%S%p': 'se', '%I%M%S %p': 'se'}
for p, u in time12_pat.items():
try:
ts = strftime('%H:%M:%S', strptime(ts, p))
break
except:
continue
sf = p
unit = time12_pat[sf] if unit is None else unit
else:
time24_pat = {'%H:%M': 'm', '%H:%M:%S': 'se', '%H': 'h', '%H%M': 'm', '%H%M%S': 'se'}
for p, u in time24_pat.items():
try:
ts = strftime('%H:%M:%S', strptime(ts, p))
break
except:
continue
sf = p
unit = time24_pat[sf] if unit is None else unit
if 'se' in unit.lower():
frm = sf if frm is None else frm
elif 'm' in unit.lower():
frm = sf if frm is None else frm
rnd = rnd * 60
elif 'h' in unit.lower():
frm = p if frm is None else frm
rnd = rnd * 3600
secs = sum(int(x) * 60 ** i for i, x in enumerate(reversed(ts.split(':'))))
rtm = int(round(secs / rnd, 0) * rnd)
nt = strftime(frm, gmtime(rtm))
return nt
Call function as follow: Round to nearest 5 minutes with ouput format = hh:mm as follow
ts = '02:27:29'
nt = toNearestTime(ts, unit='min', rnd=5, frm='%H:%M')
print(nt)
output: '02:25'
Or round to nearest 30 minutes with ouput 12 hour format hh:mm:ss as follow
ts = '11:14PM'
nt = toNearestTime(ts, rnd=30, frm='%I:%M:%S %p')
print(nt)
output: '11:00:00 PM'
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
