'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

Solution Source
Solution 1 Community
Solution 2 Thomas John
Solution 3
Solution 4
Solution 5 Community
Solution 6
Solution 7 wizardzeb
Solution 8
Solution 9 karakfa
Solution 10 user875139
Solution 11
Solution 12