(datetime.datetime.today()-datetime.datetime.today()).days giving -1 - python

I am trying this (datetime.datetime.today()-datetime.datetime.today()).days giving -1
and expecting a value 0 instead I am getting -1. In this case I have add 1 to the result
Can someone tell me why?

timedelta objects must always have a positive number of seconds and microseconds; the way a negative delta is expressed is by using a negative number of days. The seconds and microseconds then count in the other direction from those negative days.
datetime.datetime.today() produces a full date and time, complete with microseconds. The second call is executed a fraction of a second later, and subtracting the two gives you a negative timedelta.
So as a result, you get a timedelta with -1 day difference and a positive number of seconds almost equal to a full day, except for a fraction of a second:
>>> import datetime
>>> datetime.datetime.today()
datetime.datetime(2017, 2, 21, 7, 27, 43, 523202)
>>> datetime.datetime.today() - datetime.datetime.today()
datetime.timedelta(-1, 86399, 999990)
Note the 86399 seconds and 999990 microseconds in the resulting timedelta object. The difference is not a full day, it is only 10 microseconds.
If you need an 'absolute' number, use the timedelta.total_seconds() method:
>>> (datetime.datetime.today() - datetime.datetime.today()).total_seconds()
-8e-06
Divide that again by 86400 to get a number of days:
>>> int((datetime.datetime.today() - datetime.datetime.today()).total_seconds() / 86400)
0
>>> int((datetime.datetime.today() - datetime.timedelta(days=2, seconds=10) - datetime.datetime.today()).total_seconds() / 86400)
-2
Alternatively, add 1 to the .days attribute of a timedelta when seconds or microseconds is non-zero:
td = datetime.datetime.today() - datetime.datetime.today()
days = td.days + (1 if td.seconds or td.microseconds else 0)

try to store data in a variable:
>>> a = datetime.datetime.today()
>>> a-a
datetime.timedelta(0)
>>> (a-a).days
0
You will get the zero.
Although this is the proper explanation for your answer:
Martin Pieters' Answer

There is a micro seconds difference while you are subtracting the dates.
Suppose datetime.datetime.today() is giving
>>> datetime.datetime.today()`
datetime.datetime(2017, 5, 17, 17, 33, 45, 243739)
>>> datetime.datetime.today()
datetime.datetime(2017, 5, 17, 17, 33, 45, 727018)
From this you can see that both are ran in the same seconds timestamp but micro-fraction difference.
So, when you do
(datetime.datetime.today()-datetime.datetime.today()).days
you will get -1 day as result, which is a correct result.

Related

How to round down a datetime to the nearest 5 Minutes?

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?
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
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)
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)

Python trouble converting milliseconds to datetime and back

So I have two functions for converting python datetime.datetime() objects to and from milliseconds. I cannot figure out where this is going wrong. Here's what I'm working with:
>>> import datetime
>>> def mil_to_date(mil):
"""date items from REST services are reported in milliseconds,
this function will convert milliseconds to datetime objects
Required:
mil -- time in milliseconds
"""
if mil == None:
return None
elif mil < 0:
return datetime.datetime.utcfromtimestamp(0) + datetime.timedelta(seconds=(mil/1000))
else:
return datetime.datetime.fromtimestamp(mil / 1000)
>>> def date_to_mil(date):
"""converts datetime.datetime() object to milliseconds
date -- datetime.datetime() object"""
if isinstance(date, datetime.datetime):
epoch = datetime.datetime.utcfromtimestamp(0)
return long((date - epoch).total_seconds() * 1000.0)
>>> mil = 1394462888000
>>> date = mil_to_date(mil)
>>> date
datetime.datetime(2014, 3, 10, 9, 48, 8) #this is correct
>>> d2m = date_to_mil(date)
>>> d2m
1394444888000L
>>> mil
1394462888000L
>>> date2 = mil_to_date(d2m)
>>> date2
datetime.datetime(2014, 3, 10, 4, 48, 8) #why did I lose 5 hours??
For some reason, I am losing 5 hours. Am I overlooking something obvious? Or is there a problem with one or both of my functions?
The reason for this is that date_to_mil works with UTC and mil_to_date doesn't. You should replace utcfromtimestamp with fromtimestamp.
Further explanation:
In your code, epoch is the date of the epoch in UTC (but the object is without any time-zone). But date is local since fromtimestamp returns a local time:
If optional argument tz is None or not specified, the timestamp is
converted to the platform’s local date and time, and the returned
datetime object is naive
So you subtract the UTC epoch from the local datetime and you get a delay which is your local delay to UTC.
If input is UTC then to get POSIX timestamp as integer milliseconds:
from datetime import datetime, timedelta
def timestamp_millis(utc_time, epoch=datetime(1970, 1, 1)):
"""Return milliseconds since Epoch as integer."""
td = utc_time - epoch
return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) // 10**3
Note: the formula may produce a different result from: int(td.total_seconds() * 1000).
And in reverse: to get UTC time from POSIX time given as milliseconds:
def datetime_from_millis(millis, epoch=datetime(1970, 1, 1)):
"""Return UTC time that corresponds to milliseconds since Epoch."""
return epoch + timedelta(milliseconds=millis)
It supports both positive and negative millis.
Note: None handling happens outside these functions.
Example:
>>> datetime_from_millis(1394462888000)
datetime.datetime(2014, 3, 10, 14, 48, 8)
>>> datetime.utcfromtimestamp(1394462888)
datetime.datetime(2014, 3, 10, 14, 48, 8)
>>> timestamp_millis(_)
1394462888000
The result is different from the one in your question!
gmtime(0).year != 1970 and TZ=right/UTC cases are ignored.

Why does datetime.datetime.now() - datetime.datetime.now() equal to datetime.timedelta(-1, 86399, 999974)?

Consider the following snippet:
import datetime
print(datetime.datetime.now() - datetime.datetime.now())
On my Python 2.7.8 under x86_64 Linux, I am getting -1 day, 23:59:59.999940. Why could be that?
See the datetime.timedelta documenation:
Note that normalization of negative values may be surprising at first. For example,
>>> from datetime import timedelta
>>> d = timedelta(microseconds=-1)
>>> (d.days, d.seconds, d.microseconds) (-1, 86399, 999999)
You have a negative timedelta, and normalisation always uses positive numbers for seconds and microseconds.
To store a negative delta then where only the .days attribute can store negative values, you end up with a -1 day plus a positive amount of seconds and microseconds:
>>> import datetime
>>> td = datetime.datetime.now() - datetime.datetime.now()
>>> print(td)
-1 day, 23:59:59.999988
>>> td
datetime.timedelta(-1, 86399, 999988)
>>> td.days
-1
>>> td.seconds
86399
>>> td.microseconds
999988
>>> (24 * 60 * 60) # 1 day in seconds
86400
>>> (24 * 60 * 60) - td.seconds
1
>>> 1000000 - td.microseconds
12
So the timedelta really represents -12 microseconds, but expressed relative to -1 day that becomes +86399 seconds and +999988 microseconds.

Python: Date manipulation code

With python I want to calculate the delta days of a day_of_a_year day and its corresponding month, as well delta days for month + 1.
*Sorry I forgot to mention that the year is a known variable
eg.
def a(day_of_year):
<...>
return [(days_from_start_of_month),(days_untill_end_of_month)]
so
If
day_of_year = 32
a(32) = (2,28) #assuming the month which the day_of_year corresponds to starts from day 30 and ends to day 60.
So far im studying the datetime , timeutils and calendar modules and I really can't figure out the logic for the code! I wish i had something solid to show, but Im getting lost somewhere in timedelta functions.
The first day of the month is easy to construct, as is the first day of the next month. Once you have those, the rest is even easier. As pointed out by the OP, the calendar.monthrange function gives us the most readable method to get the last day of a month.
>>> from datetime import date, year
>>> import calendar
>>> def first_day(dt):
... # Simply copy year and month into new date instance
... return date(dt.year, dt.month, 1)
...
>>> def last_day(dt):
... days_in_month = calendar.monthrange(dt.year, dt.month)[1]
... return date(dt.year, dt.month, days_in_month)
...
>>> nth_day = 32
>>> day_of_year = date(2012, 1, 1) + timedelta(days=nth_day - 1)
>>> day_of_year
datetime.date(2012, 2, 1)
>>> first_day(day_of_year), last_day(day_of_year)
(datetime.date(2012, 2, 1), datetime.date(2012, 2, 29))
>>> day_of_year - first_day(day_of_year), last_day(day_of_year) - day_of_year
(datetime.timedelta(0), datetime.timedelta(28))
To combine these techniques into one function:
def delta_to_start_and_end(year, day_of_year):
dt = date(year, 1, 1) + timedelta(days=(day_of_year - 1))
def first_day(dt):
return date(dt.year, dt.month, 1)
def last_day(dt):
days_in_month = calendar.monthrange(dt.year, dt.month)[1]
return date(dt.year, dt.month, days_in_month)
return (dt - first_day(dt)).days, (last_day(dt) - dt).days
Output:
>>> delta_to_start_and_end(2012, 32)
(0, 28)
>>> delta_to_start_and_end(2011, 32)
(0, 27)
>>> delta_to_start_and_end(2012, 34)
(2, 26)
>>> delta_to_start_and_end(2012, 364)
(28, 2)
I'm not sure if you want to add 1 to each of these two values; currently the first day of the month (first example) gives you 0 as the first value and (days-in-the-month - 1) as the second value, as this is the difference in days from those points. It's trivial to add + 1 twice on the last line of the delta_to_start_and_end function if you need these.
As a historic note, a previous version of this answer used a different method to calculate the last day of a month without the calendar module:
def last_day(dt):
rest, month = divmod(dt.month, 12)
return date(dt.year + rest, month + 1, 1) - timedelta(days=1)
This function uses the divmod builtin function to handle the 'current month is December' edge-case; in that case the next month is not 13, but 1 and we'd need to increase the year by one as well. Rolling over a number back to the 'start' is the modulus of the number, but the divmod function gives us the divisor as well, which happens to be 1 if the current month is 12. This gives us a handy indicator when to increase the year.
I don't think that there's an existing library that works for this. You have to make something yourself, like this:
monthdays = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
day = 32
total = 0
for i in monthdays:
if day - total - i < 0:
before = day - total
after = total + i - day
break
total += i
print before, after
(just a quick start, there is possibly a more elegant way)

How to round the minute of a datetime object

I have a datetime object produced using strptime().
>>> tm
datetime.datetime(2010, 6, 10, 3, 56, 23)
What I need to do is round the minute to the closest 10th minute. What I have been doing up to this point was taking the minute value and using round() on it.
min = round(tm.minute, -1)
However, as with the above example, it gives an invalid time when the minute value is greater than 56. i.e.: 3:60
What is a better way to do this? Does datetime support this?
This will get the 'floor' of a datetime object stored in tm rounded to the 10 minute mark before tm.
tm = tm - datetime.timedelta(minutes=tm.minute % 10,
seconds=tm.second,
microseconds=tm.microsecond)
If you want classic rounding to the nearest 10 minute mark, do this:
discard = datetime.timedelta(minutes=tm.minute % 10,
seconds=tm.second,
microseconds=tm.microsecond)
tm -= discard
if discard >= datetime.timedelta(minutes=5):
tm += datetime.timedelta(minutes=10)
or this:
tm += datetime.timedelta(minutes=5)
tm -= datetime.timedelta(minutes=tm.minute % 10,
seconds=tm.second,
microseconds=tm.microsecond)
General function to round a datetime at any time lapse in seconds:
def roundTime(dt=None, roundTo=60):
"""Round a datetime object to any time lapse in seconds
dt : datetime.datetime object, default now.
roundTo : Closest number of seconds to round to, default 1 minute.
Author: Thierry Husson 2012 - Use it as you want but don't blame me.
"""
if dt == None : dt = datetime.datetime.now()
seconds = (dt.replace(tzinfo=None) - dt.min).seconds
rounding = (seconds+roundTo/2) // roundTo * roundTo
return dt + datetime.timedelta(0,rounding-seconds,-dt.microsecond)
Samples with 1 hour rounding & 30 minutes rounding:
print roundTime(datetime.datetime(2012,12,31,23,44,59,1234),roundTo=60*60)
2013-01-01 00:00:00
print roundTime(datetime.datetime(2012,12,31,23,44,59,1234),roundTo=30*60)
2012-12-31 23:30:00
I used Stijn Nevens code (thank you Stijn) and have a little add-on to share. Rounding up, down and rounding to nearest.
update 2019-03-09 = comment Spinxz incorporated; thank you.
update 2019-12-27 = comment Bart incorporated; thank you.
Tested for date_delta of "X hours" or "X minutes" or "X seconds".
import datetime
def round_time(dt=None, date_delta=datetime.timedelta(minutes=1), to='average'):
"""
Round a datetime object to a multiple of a timedelta
dt : datetime.datetime object, default now.
dateDelta : timedelta object, we round to a multiple of this, default 1 minute.
from: http://stackoverflow.com/questions/3463930/how-to-round-the-minute-of-a-datetime-object-python
"""
round_to = date_delta.total_seconds()
if dt is None:
dt = datetime.now()
seconds = (dt - dt.min).seconds
if seconds % round_to == 0 and dt.microsecond == 0:
rounding = (seconds + round_to / 2) // round_to * round_to
else:
if to == 'up':
# // is a floor division, not a comment on following line (like in javascript):
rounding = (seconds + dt.microsecond/1000000 + round_to) // round_to * round_to
elif to == 'down':
rounding = seconds // round_to * round_to
else:
rounding = (seconds + round_to / 2) // round_to * round_to
return dt + datetime.timedelta(0, rounding - seconds, - dt.microsecond)
# test data
print(round_time(datetime.datetime(2019,11,1,14,39,00), date_delta=datetime.timedelta(seconds=30), to='up'))
print(round_time(datetime.datetime(2019,11,2,14,39,00,1), date_delta=datetime.timedelta(seconds=30), to='up'))
print(round_time(datetime.datetime(2019,11,3,14,39,00,776980), date_delta=datetime.timedelta(seconds=30), to='up'))
print(round_time(datetime.datetime(2019,11,4,14,39,29,776980), date_delta=datetime.timedelta(seconds=30), to='up'))
print(round_time(datetime.datetime(2018,11,5,14,39,00,776980), date_delta=datetime.timedelta(seconds=30), to='down'))
print(round_time(datetime.datetime(2018,11,6,14,38,59,776980), date_delta=datetime.timedelta(seconds=30), to='down'))
print(round_time(datetime.datetime(2017,11,7,14,39,15), date_delta=datetime.timedelta(seconds=30), to='average'))
print(round_time(datetime.datetime(2017,11,8,14,39,14,999999), date_delta=datetime.timedelta(seconds=30), to='average'))
print(round_time(datetime.datetime(2019,11,9,14,39,14,999999), date_delta=datetime.timedelta(seconds=30), to='up'))
print(round_time(datetime.datetime(2012,12,10,23,44,59,7769),to='average'))
print(round_time(datetime.datetime(2012,12,11,23,44,59,7769),to='up'))
print(round_time(datetime.datetime(2010,12,12,23,44,59,7769),to='down',date_delta=datetime.timedelta(seconds=1)))
print(round_time(datetime.datetime(2011,12,13,23,44,59,7769),to='up',date_delta=datetime.timedelta(seconds=1)))
print(round_time(datetime.datetime(2012,12,14,23,44,59),date_delta=datetime.timedelta(hours=1),to='down'))
print(round_time(datetime.datetime(2012,12,15,23,44,59),date_delta=datetime.timedelta(hours=1),to='up'))
print(round_time(datetime.datetime(2012,12,16,23,44,59),date_delta=datetime.timedelta(hours=1)))
print(round_time(datetime.datetime(2012,12,17,23,00,00),date_delta=datetime.timedelta(hours=1),to='down'))
print(round_time(datetime.datetime(2012,12,18,23,00,00),date_delta=datetime.timedelta(hours=1),to='up'))
print(round_time(datetime.datetime(2012,12,19,23,00,00),date_delta=datetime.timedelta(hours=1)))
From the best answer I modified to an adapted version using only datetime objects, this avoids having to do the conversion to seconds and makes the calling code more readable:
def roundTime(dt=None, dateDelta=datetime.timedelta(minutes=1)):
"""Round a datetime object to a multiple of a timedelta
dt : datetime.datetime object, default now.
dateDelta : timedelta object, we round to a multiple of this, default 1 minute.
Author: Thierry Husson 2012 - Use it as you want but don't blame me.
Stijn Nevens 2014 - Changed to use only datetime objects as variables
"""
roundTo = dateDelta.total_seconds()
if dt == None : dt = datetime.datetime.now()
seconds = (dt - dt.min).seconds
# // is a floor division, not a comment on following line:
rounding = (seconds+roundTo/2) // roundTo * roundTo
return dt + datetime.timedelta(0,rounding-seconds,-dt.microsecond)
Samples with 1 hour rounding & 15 minutes rounding:
print roundTime(datetime.datetime(2012,12,31,23,44,59),datetime.timedelta(hour=1))
2013-01-01 00:00:00
print roundTime(datetime.datetime(2012,12,31,23,44,49),datetime.timedelta(minutes=15))
2012-12-31 23:30:00
Pandas has a datetime round feature, but as with most things in Pandas it needs to be in Series format.
>>> ts = pd.Series(pd.date_range(Dt(2019,1,1,1,1),Dt(2019,1,1,1,4),periods=8))
>>> print(ts)
0 2019-01-01 01:01:00.000000000
1 2019-01-01 01:01:25.714285714
2 2019-01-01 01:01:51.428571428
3 2019-01-01 01:02:17.142857142
4 2019-01-01 01:02:42.857142857
5 2019-01-01 01:03:08.571428571
6 2019-01-01 01:03:34.285714285
7 2019-01-01 01:04:00.000000000
dtype: datetime64[ns]
>>> ts.dt.round('1min')
0 2019-01-01 01:01:00
1 2019-01-01 01:01:00
2 2019-01-01 01:02:00
3 2019-01-01 01:02:00
4 2019-01-01 01:03:00
5 2019-01-01 01:03:00
6 2019-01-01 01:04:00
7 2019-01-01 01:04:00
dtype: datetime64[ns]
Docs - Change the frequency string as needed.
Here is a simpler generalized solution without floating point precision issues and external library dependencies:
import datetime
def time_mod(time, delta, epoch=None):
if epoch is None:
epoch = datetime.datetime(1970, 1, 1, tzinfo=time.tzinfo)
return (time - epoch) % delta
def time_round(time, delta, epoch=None):
mod = time_mod(time, delta, epoch)
if mod < delta / 2:
return time - mod
return time + (delta - mod)
def time_floor(time, delta, epoch=None):
mod = time_mod(time, delta, epoch)
return time - mod
def time_ceil(time, delta, epoch=None):
mod = time_mod(time, delta, epoch)
if mod:
return time + (delta - mod)
return time
In your case:
>>> tm = datetime.datetime(2010, 6, 10, 3, 56, 23)
>>> time_round(tm, datetime.timedelta(minutes=10))
datetime.datetime(2010, 6, 10, 4, 0)
>>> time_floor(tm, datetime.timedelta(minutes=10))
datetime.datetime(2010, 6, 10, 3, 50)
>>> time_ceil(tm, datetime.timedelta(minutes=10))
datetime.datetime(2010, 6, 10, 4, 0)
if you don't want to use condition, you can use modulo operator:
minutes = int(round(tm.minute, -1)) % 60
UPDATE
did you want something like this?
def timeround10(dt):
a, b = divmod(round(dt.minute, -1), 60)
return '%i:%02i' % ((dt.hour + a) % 24, b)
timeround10(datetime.datetime(2010, 1, 1, 0, 56, 0)) # 0:56
# -> 1:00
timeround10(datetime.datetime(2010, 1, 1, 23, 56, 0)) # 23:56
# -> 0:00
.. if you want result as string. for obtaining datetime result, it's better to use timedelta - see other responses ;)
i'm using this. it has the advantage of working with tz aware datetimes.
def round_minutes(some_datetime: datetime, step: int):
""" round up to nearest step-minutes """
if step > 60:
raise AttrbuteError("step must be less than 60")
change = timedelta(
minutes= some_datetime.minute % step,
seconds=some_datetime.second,
microseconds=some_datetime.microsecond
)
if change > timedelta():
change -= timedelta(minutes=step)
return some_datetime - change
it has the disadvantage of only working for timeslices less than an hour.
A straightforward approach:
def round_time(dt, round_to_seconds=60):
"""Round a datetime object to any number of seconds
dt: datetime.datetime object
round_to_seconds: closest number of seconds for rounding, Default 1 minute.
"""
rounded_epoch = round(dt.timestamp() / round_to_seconds) * round_to_seconds
rounded_dt = datetime.datetime.fromtimestamp(rounded_epoch).astimezone(dt.tzinfo)
return rounded_dt
This will do it, I think it uses a very useful application of round.
from typing import Literal
import math
def round_datetime(dt: datetime.datetime, step: datetime.timedelta, d: Literal['no', 'up', 'down'] = 'no'):
step = step.seconds
round_f = {'no': round, 'up': math.ceil, 'down': math.floor}
return datetime.datetime.fromtimestamp(step * round_f[d](dt.timestamp() / step))
date = datetime.datetime(year=2022, month=11, day=16, hour=10, minute=2, second=30, microsecond=424242)#
print('Original:', date)
print('Standard:', round_datetime(date, datetime.timedelta(minutes=5)))
print('Down: ', round_datetime(date, datetime.timedelta(minutes=5), d='down'))
print('Up: ', round_datetime(date, datetime.timedelta(minutes=5), d='up'))
The result:
Original: 2022-11-16 10:02:30.424242
Standard: 2022-11-16 10:05:00
Down: 2022-11-16 10:00:00
Up: 2022-11-16 10:05:00
yes, if your data belongs to a DateTime column in a pandas series, you can round it up using the built-in pandas.Series.dt.round function.
See documentation here on pandas.Series.dt.round.
In your case of rounding to 10min it will be Series.dt.round('10min') or Series.dt.round('600s') like so:
pandas.Series(tm).dt.round('10min')
Edit to add Example code:
import datetime
import pandas
tm = datetime.datetime(2010, 6, 10, 3, 56, 23)
tm_rounded = pandas.Series(tm).dt.round('10min')
print(tm_rounded)
>>> 0 2010-06-10 04:00:00
dtype: datetime64[ns]
I came up with this very simple function, working with any timedelta as long as it's either a multiple or divider of 60 seconds. It's also compatible with timezone-aware datetimes.
#!/usr/env python3
from datetime import datetime, timedelta
def round_dt_to_delta(dt, delta=timedelta(minutes=30)):
ref = datetime.min.replace(tzinfo=dt.tzinfo)
return ref + round((dt - ref) / delta) * delta
Output:
In [1]: round_dt_to_delta(datetime(2012,12,31,23,44,49), timedelta(seconds=15))
Out[1]: datetime.datetime(2012, 12, 31, 23, 44, 45)
In [2]: round_dt_to_delta(datetime(2012,12,31,23,44,49), timedelta(minutes=15))
Out[2]: datetime.datetime(2012, 12, 31, 23, 45)
General Function to round down times of minutes:
from datetime import datetime
def round_minute(date: datetime = None, round_to: int = 1):
"""
round datetime object to minutes
"""
if not date:
date = datetime.now()
date = date.replace(second=0, microsecond=0)
delta = date.minute % round_to
return date.replace(minute=date.minute - delta)
Those seem overly complex
def round_down_to():
num = int(datetime.utcnow().replace(second=0, microsecond=0).minute)
return num - (num%10)
def get_rounded_datetime(self, dt, freq, nearest_type='inf'):
if freq.lower() == '1h':
round_to = 3600
elif freq.lower() == '3h':
round_to = 3 * 3600
elif freq.lower() == '6h':
round_to = 6 * 3600
else:
raise NotImplementedError("Freq %s is not handled yet" % freq)
# // is a floor division, not a comment on following line:
seconds_from_midnight = dt.hour * 3600 + dt.minute * 60 + dt.second
if nearest_type == 'inf':
rounded_sec = int(seconds_from_midnight / round_to) * round_to
elif nearest_type == 'sup':
rounded_sec = (int(seconds_from_midnight / round_to) + 1) * round_to
else:
raise IllegalArgumentException("nearest_type should be 'inf' or 'sup'")
dt_midnight = datetime.datetime(dt.year, dt.month, dt.day)
return dt_midnight + datetime.timedelta(0, rounded_sec)
Based on Stijn Nevens and modified for Django use to round current time to the nearest 15 minute.
from datetime import date, timedelta, datetime, time
def roundTime(dt=None, dateDelta=timedelta(minutes=1)):
roundTo = dateDelta.total_seconds()
if dt == None : dt = datetime.now()
seconds = (dt - dt.min).seconds
# // is a floor division, not a comment on following line:
rounding = (seconds+roundTo/2) // roundTo * roundTo
return dt + timedelta(0,rounding-seconds,-dt.microsecond)
dt = roundTime(datetime.now(),timedelta(minutes=15)).strftime('%H:%M:%S')
dt = 11:45:00
if you need full date and time just remove the .strftime('%H:%M:%S')
Not the best for speed when the exception is caught, however this would work.
def _minute10(dt=datetime.utcnow()):
try:
return dt.replace(minute=round(dt.minute, -1))
except ValueError:
return dt.replace(minute=0) + timedelta(hours=1)
Timings
%timeit _minute10(datetime(2016, 12, 31, 23, 55))
100000 loops, best of 3: 5.12 µs per loop
%timeit _minute10(datetime(2016, 12, 31, 23, 31))
100000 loops, best of 3: 2.21 µs per loop
A two line intuitive solution to round to a given time unit, here seconds, for a datetime object t:
format_str = '%Y-%m-%d %H:%M:%S'
t_rounded = datetime.strptime(datetime.strftime(t, format_str), format_str)
If you wish to round to a different unit simply alter format_str.
This approach does not round to arbitrary time amounts as above methods, but is a nicely Pythonic way to round to a given hour, minute or second.
Other solution:
def round_time(timestamp=None, lapse=0):
"""
Round a timestamp to a lapse according to specified minutes
Usage:
>>> import datetime, math
>>> round_time(datetime.datetime(2010, 6, 10, 3, 56, 23), 0)
datetime.datetime(2010, 6, 10, 3, 56)
>>> round_time(datetime.datetime(2010, 6, 10, 3, 56, 23), 1)
datetime.datetime(2010, 6, 10, 3, 57)
>>> round_time(datetime.datetime(2010, 6, 10, 3, 56, 23), -1)
datetime.datetime(2010, 6, 10, 3, 55)
>>> round_time(datetime.datetime(2019, 3, 11, 9, 22, 11), 3)
datetime.datetime(2019, 3, 11, 9, 24)
>>> round_time(datetime.datetime(2019, 3, 11, 9, 22, 11), 3*60)
datetime.datetime(2019, 3, 11, 12, 0)
>>> round_time(datetime.datetime(2019, 3, 11, 10, 0, 0), 3)
datetime.datetime(2019, 3, 11, 10, 0)
:param timestamp: Timestamp to round (default: now)
:param lapse: Lapse to round in minutes (default: 0)
"""
t = timestamp or datetime.datetime.now() # type: Union[datetime, Any]
surplus = datetime.timedelta(seconds=t.second, microseconds=t.microsecond)
t -= surplus
try:
mod = t.minute % lapse
except ZeroDivisionError:
return t
if mod: # minutes % lapse != 0
t += datetime.timedelta(minutes=math.ceil(t.minute / lapse) * lapse - t.minute)
elif surplus != datetime.timedelta() or lapse < 0:
t += datetime.timedelta(minutes=(t.minute / lapse + 1) * lapse - t.minute)
return t
Hope this helps!
The shortest way I know
min = tm.minute // 10 * 10
Most of the answers seem to be too complicated for such a simple question.
Assuming your_time is the datetime object your have, the following rounds (actually floors) it at a desired resolution defined in minutes.
from math import floor
your_time = datetime.datetime.now()
g = 10 # granularity in minutes
print(
datetime.datetime.fromtimestamp(
floor(your_time.timestamp() / (60*g)) * (60*g)
))
The function below with minimum of import will do the job. You can round to anything you want by setting te parameters unit, rnd, and frm. Play with the function and you will see how easy it will be.
def toNearestTime(ts, unit='sec', rnd=1, frm=None):
''' round to nearest Time format
param ts = time string to round in '%H:%M:%S' or '%H:%M' format :
param unit = specify unit wich must be rounded 'sec' or 'min' or 'hour', default is seconds :
param rnd = to which number you will round, the default is 1 :
param frm = the output (return) format of the time string, as default the function take the unit format'''
from time import strftime, gmtime
ts = ts + ':00' if len(ts) == 5 else ts
if 'se' in unit.lower():
frm = '%H:%M:%S' if frm is None else frm
elif 'm' in unit.lower():
frm = '%H:%M' if frm is None else frm
rnd = rnd * 60
elif 'h' in unit.lower():
frm = '%H' 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 default ouput format = hh:mm as follow
ts = '02:27:29'
nt = toNearestTime(ts, unit='min', rnd=5)
print(nt)
output: '02:25'
Or round to nearest hour with ouput format hh:mm:ss as follow
ts = '10:30:01'
nt = toNearestTime(ts, unit='hour', rnd=1, frm='%H:%M:%S')
print(nt)
output: '11:00:00'
last updated version

Categories