One option is to install the module dateutil
(pip install python-dateutil
) and use a relativedelta
, that calculates the difference between two datetime
's the way you wish:
from datetime import datetime
from dateutil.relativedelta import relativedelta
d1 = datetime(2014, 7, 16, 23)
d2 = datetime(2019, 5, 10, 17, 30, 45)
# diferença entre d1 e d2
diff = relativedelta(d2, d1)
print("{} anos, {} meses, {} dias, {} horas, {} minutos, {} segundos"
.format(diff.years, diff.months, diff.days, diff.hours, diff.minutes, diff.seconds))
# a partir do Python 3.7, é possível usar f-strings
print(f"{diff.years} anos, {diff.months} meses, {diff.days} dias, {diff.hours} horas, {diff.minutes} minutos, {diff.seconds} segundos")
The result is an object that has the difference properly "broken" in fields, the way you need it. The output is:
4 years, 9 months, 23 days, 18 hours, 30 minutes, 45 seconds
Date arithmetic is not obvious
It is worth remembering that date arithmetic is not at all obvious, and is often quite counterintuitive. To difference in years and months, for example, you may have several cases whose results vary depending on the dates involved.
For example, the difference between 1 January and 1 February is 1 month, as well as the difference between 1 February and 1 March:
# diferença entre 1 de janeiro e 1 de fevereiro
print(relativedelta(datetime(2019, 2, 1), datetime(2019, 1, 1)))
# diferença entre 1 de fevereiro e 1 de março
print(relativedelta(datetime(2019, 3, 1), datetime(2019, 2, 1)))
Both result in a relativedelta
1 month, and the above code prints twice the same thing:
relativedelta(months=+1)
Even if between 01/01/2019 and 01/02/2019 has 31 days, and between 01/02/2019 and 01/03/2019 has 28 days, the relativedelta
considers that in both cases the difference is 1 month. This happens because months have varied sizes and the equivalent difference in days may vary according to the dates involved.
Another example, the difference between 28, 29, 30 or 31 January 2019 and 28 February 2019:
d2 = datetime(2019, 2, 28)
print(relativedelta(d2, datetime(2019, 1, 28)))
print(relativedelta(d2, datetime(2019, 1, 29)))
print(relativedelta(d2, datetime(2019, 1, 30)))
print(relativedelta(d2, datetime(2019, 1, 31)))
In all cases above, the difference is exactly one month. The above code prints 4 times the same relativedelta
:
relativedelta(months=+1)
This happens because the months can have 28, 29, 30 or 31 days. If I add 1 month to 31 January 2019, the result should be February 31, 2019. But since February is not 31 days old, this value is adjusted to February 28:
print(datetime(2019, 2, 28) + relativedelta(month=1)) # 2019-01-28 00:00:00
Not all API’s/languages make this adjustment. In Javascript, for example, the result is a date in March.
The same happens if I add 1 month to 28, 29 and 30 January 2019: the result is 28 February 2019. Therefore, the difference between the dates shown above is always 1 month.
It is worth remembering that the above cases give exactly 1 month because there was no time set for each datetime
(and by default it is set to midnight, which means that all the above dates have the time equal to midnight). But if we set a time on the dates:
d2 = datetime(2019, 2, 28)
print(relativedelta(d2, datetime(2019, 1, 28, 10, 30)))
print(relativedelta(d2, datetime(2019, 1, 29, 10, 30)))
print(relativedelta(d2, datetime(2019, 1, 30, 10, 30)))
print(relativedelta(d2, datetime(2019, 1, 31, 10, 30)))
In case, I have set the time of 10:30 at the initial date, but at the final date I kept the default (midnight). This means that, as it has not yet arrived at 10:30 on the 28th/2nd, it has not yet completed a month (because the relativedelta
also takes into account the time). So the difference is no more than 1 month, and the departure is:
relativedelta(days=+30, hours=+13, minutes=+30)
relativedelta(days=+29, hours=+13, minutes=+30)
relativedelta(days=+28, hours=+13, minutes=+30)
relativedelta(days=+27, hours=+13, minutes=+30)
There is also the case of the difference in years, when there are leap years involved. For example, what is the difference between 02/29/2016 and 02/28/2017?
print(relativedelta(datetime(2017, 2, 28), datetime(2016, 2, 29)))
In this case, the difference is 1 year:
relativedelta(years=+1)
This is a choice of API implementation, and can vary from one language to another. In Java >= 8, in API java.time
, for example, the difference between these dates is zero years, because the API understands that only from 29/02 it completes 1 year (and in this case, as it is not leap year, only from 01/03 onwards is considered that 1 year has passed).
Which of the Apis is right? Both, because there is no definite rule for date arithmetic (such as exists in mathematics). This is because months and years do not have fixed sizes (months may be 28 to 31 days, years may be 365 or 366 days), and the differences always depend on the dates involved. In mathematics a hundred always have 10 tens, but in date arithmetic we can have "a hundred with 10 or 11 tens" (be it a month with 28, 29, 30 or 31 days, or a year with 365 or 366 days), depending on the case.
In the case of leap year, just think of someone who was born on 02/29/2016. On 02/28/2017, we could already consider that this person is 1 year old? Or only on 01/03/2017? The relativedelta
believes that on 28/02/2017, 1 year has passed, the java.time
understands that.
In this case, there is no right answer: what we have are implementation decisions, and it is up to you to know how the API you are using works, and if you want different results, make the necessary adjustments manually.
And what exactly would be the expected exit?
3-0-12 15:3:35
? That doesn’t make any sense.– Woss
@Andersoncarloswoss, exact format would be anything like: X years, Y months, K days, Z hours, W minutes, S secs. I will correct above in question, my mistake
– Hula Hula
Something like that? http://ideone.com/3zmzN9
– Woss
Exact @Andersoncarloswoss, is there any way to be more accurate? For example 31-day months and/or bisex years... But yes that’s right
– Hula Hula
There are ways, but the work will be much bigger, the idea is the same that I used. And it is not a redeclaration of the variable, only the updating of its value. When I calculate the number of hours, you need to discount that total number of seconds not to be considered twice.
– Woss
Thank you @Andersoncarloswoss, you can reply that your solution is correct, I will accept it because it helped me. Thank you
– Hula Hula