Rounding time in Python
Asked Answered
C

9

35

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
Copula answered 24/7, 2011 at 11:21 Comment(4)
related: #3464430Tallula
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)?Meany
Sorry; For 'date' read 'time'Meany
related: Rounding up to nearest 30 minutes in pythonAquaplane
L
18

For a datetime.datetime rounding, see this function: https://mcmap.net/q/167617/-how-to-round-the-minute-of-a-datetime-object

Sample of use:

print roundTime(datetime.datetime(2012,12,31,23,44,59,1234),roundTo=60*60)
2013-01-01 00:00:00
Labefaction answered 1/6, 2012 at 16:44 Comment(0)
O
17

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
Ossify answered 24/7, 2011 at 12:5 Comment(1)
That would work with datetime.time but not with datetime.datetime as you didn't take into account the dateCopula
P
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)
Pinkiepinkish answered 27/7, 2016 at 17:15 Comment(0)
S
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
Skivvy answered 24/7, 2011 at 12:1 Comment(0)
K
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)
Knotgrass answered 24/11, 2013 at 18:40 Comment(3)
that doesn't answer the question at allCopula
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.Crosslink
Careful: if tNow is already rounded to the nearest hour, then this will increment by an hour instead of doing nothing.Faludi
S
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.

Spoilsport answered 24/7, 2011 at 11:52 Comment(1)
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.Suggest
I
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.

Intoxication answered 12/10, 2018 at 14:36 Comment(0)
R
0
import pandas as pd
import datetime as dt
import re
>>> re.sub('000$','', str( pd.Series( dt.datetime.now()).round('ms')[0] ) )
Out[38]: '2024-07-24 10:25:05.816'

Very ugly, but avoids defining a function if the usage is just a one-off.

Ringmaster answered 24/7 at 14:31 Comment(0)
P
-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)
Polyurethane answered 15/8, 2022 at 18:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.