Know how many years, months, days, hours, etc have passed since a certain date

Asked

Viewed 5,347 times

3

I would like to know how do I make the output of the difference between two dates stay the way I want, in this case I would like it to be:

Since 16 - 07 - 2014 23:00:00 passed: X years, Y months, K days, Z hours, W minutes, S secs

What do I have:

import datetime
d1 = datetime.datetime(2014,7,16,23)
d2 = datetime.datetime.now()

diff = d2 - d1

print(diff) # este não é o output que quero.
  • And what exactly would be the expected exit? 3-0-12 15:3:35? That doesn’t make any sense.

  • @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

  • Something like that? http://ideone.com/3zmzN9

  • Exact @Andersoncarloswoss, is there any way to be more accurate? For example 31-day months and/or bisex years... But yes that’s right

  • 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.

  • Thank you @Andersoncarloswoss, you can reply that your solution is correct, I will accept it because it helped me. Thank you

Show 1 more comment

2 answers

4


In a very simplified way you can do:

import datetime

d1 = datetime.datetime(2014,7,16,23)
d2 = datetime.datetime.now()

diff = d2 - d1

days = diff.days
years, days = days // 365, days % 365
months, days = days // 30, days % 30

seconds = diff.seconds
hours, seconds = seconds // 3600, seconds % 3600
minutes, seconds = seconds // 60, seconds % 60

print("Desde {} passaram {} anos, {} meses, {} dias, {} horas, {} minutos e {} segundos".format(d1, years, months, days, hours, minutes, seconds))

In the object diff we will have the difference between the dates. By the attribute day and seconds we take this difference in relation to the number of days and seconds respectively. To know the number of years, we calculate the entire division between the number of days per 365 and update the value of days to discount the amount relative to those years. With the month, we calculate the division by 30 and update again the amount of days.

For hours and minutes the logic is exactly the same, dividing the number of seconds by 3600 and 60, respectively.

It is worth remembering that this difference is approximate, because it does not take into consideration leap years within the considered interval, nor the exact amount of days in each month.

See working on Ideone.

1

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.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.