34

What would be an elegant, efficient and Pythonic way to perform a h/m/s rounding operation on time related types in Python with control over the rounding resolution?

My guess is that it would require a time modulo operation. Illustrative examples:

  • 20:11:13 % (10 seconds) => (3 seconds)
  • 20:11:13 % (10 minutes) => (1 minutes and 13 seconds)

Relevant time related types I can think of:

  • datetime.datetime \ datetime.time
  • struct_time
Jonathan Livni
  • 101,334
  • 104
  • 266
  • 359
  • 1
    related: http://stackoverflow.com/questions/3463930/how-to-round-the-minute-of-a-datetime-object-python – Jacob Jul 24 '11 at 11:39
  • Do you want to round a date to the nearest 'part' (i.e. 20:11:10 rounded to nearest hour yields 20:00:00) or - as your example suggests - get the _remainder_ after rounding to the nearest part (i.e. 20:11:10 to nearest hour yields 11:13)? – Rob Cowie Jul 24 '11 at 11:48
  • Sorry; For 'date' read 'time' – Rob Cowie Jul 24 '11 at 11:57
  • related: [Rounding up to nearest 30 minutes in python](http://stackoverflow.com/q/32723150/4279) – jfs Dec 15 '15 at 14:54

8 Answers8

19

For a datetime.datetime rounding, see this function: https://stackoverflow.com/a/10854034/1431079

Sample of use:

print roundTime(datetime.datetime(2012,12,31,23,44,59,1234),roundTo=60*60)
2013-01-01 00:00:00
Community
  • 1
  • 1
Le Droid
  • 4,534
  • 3
  • 37
  • 32
16

How about use datetime.timedeltas:

import time
import datetime as dt

hms=dt.timedelta(hours=20,minutes=11,seconds=13)

resolution=dt.timedelta(seconds=10)
print(dt.timedelta(seconds=hms.seconds%resolution.seconds))
# 0:00:03

resolution=dt.timedelta(minutes=10)
print(dt.timedelta(seconds=hms.seconds%resolution.seconds))
# 0:01:13
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
5

This will round up time data to a resolution as asked in the question:

import datetime as dt

current = dt.datetime.now()
current_td = dt.timedelta(
    hours = current.hour, 
    minutes = current.minute, 
    seconds = current.second, 
    microseconds = current.microsecond)

# to seconds resolution
to_sec = dt.timedelta(seconds = round(current_td.total_seconds()))
print(dt.datetime.combine(current, dt.time(0)) + to_sec)

# to minute resolution
to_min = dt.timedelta(minutes = round(current_td.total_seconds() / 60))
print(dt.datetime.combine(current, dt.time(0)) + to_min)

# to hour resolution
to_hour = dt.timedelta(hours = round(current_td.total_seconds() / 3600))
print(dt.datetime.combine(current, dt.time(0)) + to_hour)
caiohamamura
  • 2,260
  • 21
  • 23
3

I use following code snippet to round to the next hour:

import datetime as dt

tNow  = dt.datetime.now()
# round to the next full hour
tNow -= dt.timedelta(minutes = tNow.minute, seconds = tNow.second, microseconds =  tNow.microsecond)
tNow += dt.timedelta(hours = 1)
Liondancer
  • 15,721
  • 51
  • 149
  • 255
PTH
  • 31
  • 1
  • that doesn't answer the question at all – Jonathan Livni Nov 24 '13 at 21:04
  • 2
    It's actually the only answer that directly answers the question title, which is about *rounding* (and happens to be what I'm looking for). The question body contradicts the title. This is just a bad question. Thanks, PTH. – stickfigure Apr 05 '15 at 15:57
  • 4
    Careful: if `tNow` is already rounded to the nearest hour, then this will increment by an hour instead of doing nothing. – Adriano May 29 '16 at 07:49
3

You can convert both times to seconds, do the modulo operati

from datetime import time

def time2seconds(t):
    return t.hour*60*60+t.minute*60+t.second

def seconds2time(t):
    n, seconds = divmod(t, 60)
    hours, minutes = divmod(n, 60)
    return time(hours, minutes, seconds)

def timemod(a, k):
    a = time2seconds(a)
    k = time2seconds(k)
    res = a % k
    return seconds2time(res)

print(timemod(time(20, 11, 13), time(0,0,10)))
print(timemod(time(20, 11, 13), time(0,10,0)))

Outputs:

00:00:03
00:01:13
utdemir
  • 26,532
  • 10
  • 62
  • 81
2

I think I'd convert the time in seconds, and use standard modulo operation from that point.

20:11:13 = 20*3600 + 11*60 + 13 = 72673 seconds

72673 % 10 = 3

72673 % (10*60) = 73

This is the easiest solution I can think about.

Pierre
  • 6,084
  • 5
  • 32
  • 52
  • If you wanted to bother with special cases, modulo n seconds, where n is in (2,3,4,5,10,12,15,20,30), can be done with just the seconds part. – PaulMcG Jul 24 '11 at 11:57
1

Here is a lossy* version of hourly rounding:

dt = datetime.datetime
now = dt.utcnow()
rounded = dt.utcfromtimestamp(round(now.timestamp() / 3600, 0) * 3600)

Same principle can be applied to different time spans.

*The above method assumes UTC is used, as any timezone information will be destroyed in conversion to timestamp.

if __name__ is None
  • 11,083
  • 17
  • 55
  • 71
-1

You could also try pandas.Timestamp.round:

import datetime
import pandas as pd

t = datetime.datetime(2012,12,31,23,44,59,1234)
print(pd.to_datetime(t).round('1min'))
% Timestamp('2012-12-31 23:45:00')

You can perform the following if you want to change the result back to datetime format:

pd.to_datetime(t).round('1min').to_pydatetime()
% datetime.datetime(2012, 12, 31, 23, 45)
Lorry
  • 382
  • 2
  • 7