I would like to known why I do not get minus hours and minus seconds from datetime.timedelta?
I have the following method
def time_diff(external_datetime, internal_datetime):
from pandas.core.indexes.period import parse_time_string # include for completeness
time_external = parse_time_string(external_datetime)[0]
time_internal = parse_time_string(internal_datetime)[0]
diff = time_external - time_internal
return diff
all is as expected when the datetimes look like these;
external_datetime = "2020-01-01T00:00:00"
internal_datetime = "2020-01-02T00:00:00"
returned is a datetime.timedelta of -1 days (datetime.timedelta(days=-1))
why then when i change the times to;
external_datetime = "2020-01-01T00:00:00"
internal_datetime = "2020-01-01T01:30:00"
do I get a diff of datetime.timedelta(days=-1, seconds=81000)
I did wonder if it was due to being 'near' midnight but
external_datetime = "2020-01-01T11:00:00"
internal_datetime = "2020-01-01T11:30:00"
results in datetime.timedelta(days=-1, seconds=84600)
versions
python 3.8.2
pandas 1.1.4
From the documentation for timedelta:
Only days, seconds and microseconds are stored internally. Arguments
are converted to those units:
A millisecond is converted to 1000 microseconds.
A minute is converted to 60 seconds.
An hour is converted to 3600 seconds.
A week is converted to 7 days.
and days, seconds and microseconds are then normalized so that the
representation is unique, with
0 <= microseconds < 1000000
0 <= seconds < 3600*24 (the number of seconds in one day)
-999999999 <= days <= 999999999
So the number of seconds and microseconds are guaranteed to be non-negative, with the number of days being positive or negative as necessary. It makes sense to let the larger unit days be positive or negative, as that will account for most of an arbitrary interval. days can be slightly more negative than necessary, with the smaller, limited units used to make up the difference.
Note that with this representation, the sign of the interval is determined solely by the sign of the days.
Time in computing is generally based on number of seconds (or some unit of seconds) past a reference time. I'd assume Python's DateTime represents data as hours, minutes, seconds, and subunits of seconds after the start of a day. Therefore, seconds will never be negative. Because it is always seconds after, it makes sense to go -1 day + 84600 seconds so that seconds are positive.
I get this strange result by substracting earlier time stamp for later one:
pd.to_datetime('2021-05-21 06:00:00') - pd.to_datetime('2021-05-21 06:02:00')
Output:
Timedelta('-1 days +23:58:00')
Expected Output:
Timedelta('-0 days 00:02:00')
What is the correct way to calculate a negative time difference? Thank you!
Timedelta('-1 days +23:58:00') is the proper representation of a negative time difference in pandas (and also in pure python)
# using pure python
from datetime import datetime
datetime(2021,5,21,6,0,0) - datetime(2021,5,21,6,2,0)
datetime.timedelta(days=-1, seconds=86280)
this is because the difference is properly calculated as -120 seconds, but individual time elements cannot exceed their moduli. the timedelta components are normalized. To represent negative 2 minutes, a negative day & positive time component are used.
from the python datetime module's documentation
and days, seconds and microseconds are then normalized so that the representation is unique, with
0 <= microseconds < 1000000
0 <= seconds < 3600*24 (the number of seconds in one day)
-999999999 <= days <= 999999999
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)
it is possible to retrieve the total seconds as a negative integer using the method Timedelta.total_seconds
We can do total_seconds
(pd.to_datetime('2021-05-21 06:00:00') - pd.to_datetime('2021-05-21 06:02:00')).total_seconds()
Out[9]: -120.0
Use abs to get the time delta:
>>> abs(pd.to_datetime('2021-05-21 06:00:00') - pd.to_datetime('2021-05-21 06:02:00'))
Timedelta('0 days 00:02:00')
Well your code is giving correct output ...
Your result is Timedelta('-1 days +23:58:00') which is equal to -24:00:00 + 23:58:00 => 2 mins
you can use np.timedelta64 to change the time delta to your desired output
as others have said, the pandas negative Timedelta object is the correct output in python.
import numpy as np
delta = pd.to_datetime('2021-05-21 06:00:00') - pd.to_datetime('2021-05-21 06:02:00')
print(delta)
Timedelta('-1 days +23:58:00')
#minutes
print(delta / np.timedelta64(1,'m')
-2.0
#seconds
delta / np.timedelta64(1,'s')
-120.0
datetime1 = '2020-08-19 10:13:19'
datetime2 = '2020-08-19 19:00:00'
diff = datetime1 - datetime2
The diff is a timedelta object, with:
diff.days = -1
diff.seconds = 54766 = 15.22 hours
There are only about 9 hours diff between the two datetimes. Why does it show the number of days is '1' and 15.22 hours? How to understand the diff of two datetimes?
If you subtract the earlier datetime from the later datetime, you get a positive timedelta, as one would expect.
The other way around, you get a negative timedelata in the unusual format.
But when you calculate -1 day + 15 hours = -24 hours + 15 hours = -9 hours, the result is correct.
Of course, doing this calculation manually is not what we want.
So, either avoid subtracting a later datetime from an earlier datetime:
# to get an absolute timedelta
if datetime2 > datetime1:
print(datetime2 - datetime1)
else:
print(datetime1 - datetime2)
Or use .total_seconds():
print((datetime1 - datetime2).total_seconds())
-31601.0
print((datetime2 - datetime1).total_seconds())
31601.0
In this example, the difference between two datetime objects has a negative number of days, and a positive number of hours.
import datetime
datetime1 = datetime.datetime.fromisoformat('2020-08-19 10:13:19')
datetime2 = datetime.datetime.fromisoformat('2020-08-19 19:00:00')
print(datetime1 - datetime2)
-1 day, 15:13:19
# divide by timedelta() (with argument of hours, minutes, seconds, etc.
print((datetime1 - datetime2) / datetime.timedelta(hours=1)) # in hours
-8.778055555555556
Here is an interesting interview with the core developer who maintains date / time in CPython: https://talkpython.fm/episodes/show/271/unlock-the-mysteries-of-time-pythons-datetime-that-is
UPDATE
You can calculate time difference in minutes, or days, or other units, by supplying a different parameter to .timedelta():
print((datetime1 - datetime2) / datetime.timedelta(minutes=1)) # in minutes
-526.68
print((datetime1 - datetime2) / datetime.timedelta(days=1)) # in days
-0.3658
I have 2 times stored in separate strings in the form of H:M I need to get the difference between these two and be able to tell how much minutes it equals to. I was trying datetime and timedelta, but I'm only a beginner and I don't really understand how that works. I'm getting attribute errors everytime.
So I have a and b times, and I have to get their difference in minutes.
E.G. if a = 14:08 and b= 14:50 the difference should be 42
How do I do that in python in the simplest way possible? also, in what formats do I need to use for each step?
I assume the difference is 42, not 4 (since there are 42 minutes between 14:08 and 14:50).
If the times always contains of a 5 character length string, than it's reasonably easy.
time1 = '14:08'
time2 = '15:03'
hours = int(time2[:2]) - int(time1[:2])
mins = int(time2[3:]) - int(time1[3:])
print(hours)
print(mins)
print(hours * 60 + mins)
Prints:
1
-5
55
hours will be the integer value of the left two digits [:1] subtraction of the second and first time
minutes will be the integer value of the right two digits [3:] subtraction of the second and first time
This prints 55 ... with your values it prints out 42 (the above example is to show it also works when moving over a whole hour.
You can use datetime.strptime
also the difference is 42 not 4 50-8==42 I assume that was a typo
from datetime import datetime
a,b = "14:08", "14:50"
#convert to datetime
time_a = datetime.strptime(a, "%H:%M")
time_b = datetime.strptime(b, "%H:%M")
#get timedelta from the difference of the two datetimes
delta = time_b - time_a
#get the minutes elapsed
minutes = (delta.seconds//60)%60
print(minutes)
#42
You can get the difference between the datetime.timedelta objects created from the given time strings a and b by subtracting the former from the latter, and use the total_seconds method to obtain the time interval in seconds, with which you can convert to minutes by dividing it by 60:
from datetime import timedelta
from operator import sub
sub(*(timedelta(**dict(zip(('hours', 'minutes'), map(int, t.split(':'))))) for t in (b, a))).total_seconds() // 60
So that given a = '29:50' and b = '30:08', this returns:
18.0
I've got a timedelta. I want the days, hours and minutes from that - either as a tuple or a dictionary... I'm not fussed.
I must have done this a dozen times in a dozen languages over the years but Python usually has a simple answer to everything so I thought I'd ask here before busting out some nauseatingly simple (yet verbose) mathematics.
Mr Fooz raises a good point.
I'm dealing with "listings" (a bit like ebay listings) where each one has a duration. I'm trying to find the time left by doing when_added + duration - now
Am I right in saying that wouldn't account for DST? If not, what's the simplest way to add/subtract an hour?
If you have a datetime.timedelta value td, td.days already gives you the "days" you want. timedelta values keep fraction-of-day as seconds (not directly hours or minutes) so you'll indeed have to perform "nauseatingly simple mathematics", e.g.:
def days_hours_minutes(td):
return td.days, td.seconds//3600, (td.seconds//60)%60
This is a bit more compact, you get the hours, minutes and seconds in two lines.
days = td.days
hours, remainder = divmod(td.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
# If you want to take into account fractions of a second
seconds += td.microseconds / 1e6
days, hours, minutes = td.days, td.seconds // 3600, td.seconds // 60 % 60
As for DST, I think the best thing is to convert both datetime objects to seconds. This way the system calculates DST for you.
>>> m13 = datetime(2010, 3, 13, 8, 0, 0) # 2010 March 13 8:00 AM
>>> m14 = datetime(2010, 3, 14, 8, 0, 0) # DST starts on this day, in my time zone
>>> mktime(m14.timetuple()) - mktime(m13.timetuple()) # difference in seconds
82800.0
>>> _/3600 # convert to hours
23.0
For all coming along and searching for an implementation:
The above posts are related to datetime.timedelta, which is sadly not having properties for hours and seconds. So far it was not mentioned, that there is a package, which is having these. You can find it here:
Check the source: https://github.com/andrewp-as-is/timedelta.py
Available via pip: https://pypi.org/project/timedelta/
Example - Calculation:
>>> import timedelta
>>> td = timedelta.Timedelta(days=2, hours=2)
# init from datetime.timedelta
>>> td = timedelta.Timedelta(datetime1 - datetime2)
Example - Properties:
>>> td = timedelta.Timedelta(days=2, hours=2)
>>> td.total.seconds
180000
>>> td.total.minutes
3000
>>> td.total.hours
50
>>> td.total.days
2
I hope this could help someone...
I don't understand
days, hours, minutes = td.days, td.seconds // 3600, td.seconds // 60 % 60
how about this
days, hours, minutes = td.days, td.seconds // 3600, td.seconds % 3600 / 60.0
You get minutes and seconds of a minute as a float.
I used the following:
delta = timedelta()
totalMinute, second = divmod(delta.seconds, 60)
hour, minute = divmod(totalMinute, 60)
print(f"{hour}h{minute:02}m{second:02}s")
Here is a little function I put together to do this right down to microseconds:
def tdToDict(td:datetime.timedelta) -> dict:
def __t(t, n):
if t < n: return (t, 0)
v = t//n
return (t - (v * n), v)
(s, h) = __t(td.seconds, 3600)
(s, m) = __t(s, 60)
(micS, milS) = __t(td.microseconds, 1000)
return {
'days': td.days
,'hours': h
,'minutes': m
,'seconds': s
,'milliseconds': milS
,'microseconds': micS
}
Here is a version that returns a tuple:
# usage: (_d, _h, _m, _s, _mils, _mics) = tdTuple(td)
def tdTuple(td:datetime.timedelta) -> tuple:
def _t(t, n):
if t < n: return (t, 0)
v = t//n
return (t - (v * n), v)
(s, h) = _t(td.seconds, 3600)
(s, m) = _t(s, 60)
(mics, mils) = _t(td.microseconds, 1000)
return (td.days, h, m, s, mics, mils)
While pandas.Timedelta does not provide these attributes directly, it indeed provide a method called total_seconds, based on which days, hours, and minutes can be easily derived:
import pandas as pd
td = pd.Timedelta("2 days 12:30:00")
minutes = td.total_seconds()/60
hours = minutes/60
days = hours/ 24
print(minutes, hours, days)
I found the easiest way is using str(timedelta). It will return a sting formatted like 3 days, 21:06:40.001000, and you can parse hours and minutes using simple string operations or regular expression.
While if you are using python datetime package, you can also code like below:
import datetime
tap_in = datetime.datetime.strptime("04:12", "%H:%M")
tap_out = datetime.datetime.strptime("18:20", "%H:%M")
num_of_hour = (tap_out - tap_in).total_seconds()/3600
num_of_hour # 14.133333333333333
This is another possible approach, though a bit wordier than those already mentioned. It maybe isn't the best approach for this scenario but it is handy to be able to obtain your time duration in a specific unit that isn't stored within the object (weeks, hours, minutes, milliseconds) and without having to remember or calculate conversion factors.
from datetime import timedelta
one_hour = timedelta(hours=1)
one_minute = timedelta(minutes=1)
print(one_hour/one_minute) # Yields 60.0
I've got a timedelta. I want the days, hours and minutes from that - either as a tuple or a dictionary... I'm not fussed.
in_time_delta = timedelta(days=2, hours=18, minutes=30)
td_d = timedelta(days=1)
td_h = timedelta(hours=1)
td_m = timedelta(minutes=1)
dmh_list = [in_time_delta.days,
(in_time_delta%td_d)//td_h,
(in_time_delta%td_h)//td_m]
Which should assign [2, 18, 30] to dmh_list
If using pandas (at least version >1.0), the Timedelta class has a components attribute that returns a named tuple with all the fields nicely laid out.
e.g.
import pandas as pd
delta = pd.Timestamp("today") - pd.Timestamp("2022-03-01")
print(delta.components)
timedelta = pd.Timestamp("today") - pd.Timestamp("2022-01-01")
print(timedelta.components)
print(timedelta.components.days)
print(timedelta.components.seconds)
will return something like:
Components(days=281, hours=2, minutes=24, seconds=3, milliseconds=72, microseconds=493, nanoseconds=0)
281
3
timedeltas have a days and seconds attribute .. you can convert them yourself with ease.