多年来两个日期之间的Pythonic差异?

时间:2022-07-08 21:31:20

Is there a more efficient way of doing this below? I want to have the difference in years between two dates as a single scalar. Any suggestions are welcome.

下面有更有效的方法吗?我希望将两个日期之间的年份差异作为单个标量。欢迎任何建议。

from datetime import datetime
start_date = datetime(2010,4,28,12,33)
end_date = datetime(2010,5,5,23,14)
difference  = end_date - start_date
difference_in_years = (difference.days + difference.seconds/86400)/365.2425

7 个解决方案

#1


51  

If you want precise results, I recommend using the dateutil library.

如果您想要精确的结果,我建议使用dateutil库。

from dateutil.relativedelta import relativedelta
difference_in_years = relativedelta(end_date, start_date).years

This is for complete years (e.g. a person's age). If you want fractional years, then add months, days, hours, ... up to the desired precision.

这是完整的年份(例如一个人的年龄)。如果你想要分数年,那么添加几个月,几天,几小时......达到所需的精度。

#2


10  

I use one of these to calculate person's age:

我用其中一个来计算人的年龄:

import datetime
dob = datetime.date(1980, 10, 10)

def age():
    today = datetime.date.today()
    years = today.year - dob.year
    if today.month < dob.month or (today.month == dob.month and today.day < dob.day):
        years -= 1
    return years

def age2():
    today = datetime.date.today()
    this_year_birthday = datetime.date(today.year, dob.month, dob.day)
    if this_year_birthday < today:
        years = today.year - dob.year
    else:
        years = today.year - dob.year - 1
    return years

#3


6  

More efficient? No, but more correct, probably. But it depends on how correct you want to be. Dates are not trivial things.

更高效?不,但更正确,可能。但这取决于你想要的正确程度。日期不是琐碎的事情。

Years do not have a constant length. Do you want the difference in leap years or normal years? :-) As you calculate you are always going to get a slightly incorrect answer. And how long is a day in years? You say 1/365.2425. Well, yeah, averaged over a thousand years, yeah. But otherwise not.

年份没有恒定的长度。你想要闰年或正常年份的差异吗? :-)按照你的计算,你总会得到一个稍微不正确的答案。多年来的一天多长时间?你说1 / 365.2425。嗯,是的,平均超过一千年,是的。但不是。

So the question doesn't really make much sense.

所以问题并没有多大意义。

To be correct you have to do this:

要纠正你必须这样做:

from datetime import datetime
from calendar import isleap
start_date = datetime(2005,4,28,12,33)
end_date = datetime(2010,5,5,23,14)
diffyears = end_date.year - start_date.year
difference  = end_date - start_date.replace(end_date.year)
days_in_year = isleap(end_date.year) and 366 or 365
difference_in_years = diffyears + (difference.days + difference.seconds/86400.0)/days_in_year

In this case that's a difference of 0.0012322917425568528 years, or 0.662 days, considering that this is not a leap year.

在这种情况下,这是相差0.0012322917425568528年,或0.662天,考虑到这不是闰年。

(and then we are ignoring microseconds. Heh.)

(然后我们忽略了微秒。嘿。)

#4


5  

To make sense of leap years, you are almost forced to break this into two parts: an integral number of years, and a fractional part. Both need to deal with leap years, but in different ways - the integral needs to deal with a starting date of February 29, and the fractional must deal with the differing number of days in a year. You want the fractional part to increment in equal amounts until it equals 1.0 at the next anniversary date, so it should be based on the number of days in the year after the end date.

为了理解闰年,你几乎*将其分为两部分:整数年和一小部分。两者都需要处理闰年,但需要以不同的方式 - 处理2月29日开始日期的整体需求,而小数必须处理一年中不同的天数。您希望小数部分以相等的数量递增,直到它在下一个周年日等于1.0,因此它应该基于结束日期之后一年中的天数。

Do you want your date range to include 1900 or 2100? Things get a little easier if you don't.

您希望您的日期范围包括1900或2100吗?如果你不这样做,事情会变得容易些。


Edit: It has taken me a long time to reason this through. The basic problem is that calendar years are not a constant size, but you're coercing them to be constant by setting them to 1.0. Any solution you come up with is going to have anomalies because of this, and you're going to have to choose which anomalies you can live with. John Machin was right.

What's the difference between 2008-02-28 and 2009-02-28? Most people would agree that it should be exactly 1.0 years. How about the difference between 2008-03-01 and 2009-03-01? Again, most people would agree that it should be exactly 1.0 years. If you choose to represent a date as a year plus a fraction of a year based on the day, it is impossible to make both of these statements true. This is the case for your original code which assumed a day was 1/365.2425 of a year, or indeed for any code which assumes a constant fraction of a year per day, even if the size of a day accounts for the years which are leap years.

2008-02-28和2009-02-28之间有什么区别?大多数人都会同意这应该是1。0年。 2008-03-01和2009-03-01之间的区别怎么样?同样,大多数人都会同意这应该是1。0年。如果您选择将日期表示为年份加上基于当天的一年中的一小部分,则无法使这两个陈述成立。原始代码的情况就是这样,假设一天是一年的1/365.2425,或者实际上对于任何假设每天一年的常数分数的代码,即使一天的大小占据了飞跃的年份年份。

My assertion that you needed to break this down into integral years and fractional years was an attempt to get around this problem. If you treat each of the previous conditions as an integral year, all you have to do is decide on which fraction to assign to any number of days left over. The problem with this scheme is that you still can't make sense of (date2-date1)+date3, because the fraction can't be resolved back to a day with any consistency.

我断言你需要将其分解为整数年和分数年,这是试图解决这个问题。如果您将之前的每个条件都视为一个完整的年份,那么您所要做的就是决定分配给剩余天数的分数。这个方案的问题在于你仍然无法理解(date2-date1)+ date3,因为无法将分数解析回具有任何一致性的一天。

Thus I am proposing yet another encoding, based on each year containing 366 days whether it is a leap year or not. The anomalies will firstly be that there can't be a date which is exactly a year (or 2 or 3) from Feb. 29 - "Sorry Johnny, you don't get a birthday this year, there's no Feb. 29" isn't always acceptable. Second is that if you try to coerce such a number back to a date, you'll have to account for non-leap years and check for the special case of Feb. 29 and convert it, probably to Mar. 1.

因此,我提议另一种编码,基于每年包含366天,无论是否是闰年。异常将首先是2月29日之前不能有一年(或2或3)的日期 - “对不起约翰尼,你今年没有生日,没有2月29日”总是可以接受的。其次,如果你试图强迫这样的数字回到日期,你将不得不考虑非闰年并检查2月29日的特殊情况并将其转换,可能是3月1日。

from datetime import datetime
from datetime import timedelta
from calendar import isleap

size_of_day = 1. / 366.
size_of_second = size_of_day / (24. * 60. * 60.)

def date_as_float(dt):
    days_from_jan1 = dt - datetime(dt.year, 1, 1)
    if not isleap(dt.year) and days_from_jan1.days >= 31+28:
        days_from_jan1 += timedelta(1)
    return dt.year + days_from_jan1.days * size_of_day + days_from_jan1.seconds * size_of_second

start_date = datetime(2010,4,28,12,33)
end_date = datetime(2010,5,5,23,14)
difference_in_years = date_as_float(end_time) - date_as_float(start_time)

I'm not suggesting that this is the solution, because I don't think a perfect solution is possible. But it has some desirable properties:

我并不是说这是解决方案,因为我认为不可能有一个完美的解决方案。但它有一些理想的属性:

  • The difference between any dates with the same month and day and time will be an exact number of years.
  • 具有相同月份,日期和时间的任何日期之间的差异将是确切的年数。

  • Adding a difference to another date will result in a value that can be converted back into a useful date.
  • 将差异添加到另一个日期将导致可以转换回有用日期的值。

#5


2  

Just do this:

这样做:

from dateutil.relativedelta import relativedelta

myBirthday = datetime.datetime(1983,5,20,0,0,0,0)
now = datetime.datetime.now()



difference = relativedelta(now, myBirthday)
print("My years: "+str(difference.years))

#6


1  

Here's a spin off of what Kostyantyn posted in his "age2" function. It's slightly shorter/cleaner and uses the traditional/colloquial meaning of an "age" or difference in years as well:

这是Kostyantyn在他的“age2”功能中发布的内容。它略短/清洁,并使用“年龄”的传统/口语含义或年份的差异:

def ageInYears( d ):
    today = datetime.date.today()
    currentYrAnniversary = datetime.date( today.year, d.month, d.day )
    return (today.year - d.year) - (1 if today < currentYrAnniversary else 0)

#7


0  

If you mean efficient in terms of code space then no, that's about the most efficient way to do that.

如果你的意思是代码空间有效,那么不,这是最有效的方法。

#1


51  

If you want precise results, I recommend using the dateutil library.

如果您想要精确的结果,我建议使用dateutil库。

from dateutil.relativedelta import relativedelta
difference_in_years = relativedelta(end_date, start_date).years

This is for complete years (e.g. a person's age). If you want fractional years, then add months, days, hours, ... up to the desired precision.

这是完整的年份(例如一个人的年龄)。如果你想要分数年,那么添加几个月,几天,几小时......达到所需的精度。

#2


10  

I use one of these to calculate person's age:

我用其中一个来计算人的年龄:

import datetime
dob = datetime.date(1980, 10, 10)

def age():
    today = datetime.date.today()
    years = today.year - dob.year
    if today.month < dob.month or (today.month == dob.month and today.day < dob.day):
        years -= 1
    return years

def age2():
    today = datetime.date.today()
    this_year_birthday = datetime.date(today.year, dob.month, dob.day)
    if this_year_birthday < today:
        years = today.year - dob.year
    else:
        years = today.year - dob.year - 1
    return years

#3


6  

More efficient? No, but more correct, probably. But it depends on how correct you want to be. Dates are not trivial things.

更高效?不,但更正确,可能。但这取决于你想要的正确程度。日期不是琐碎的事情。

Years do not have a constant length. Do you want the difference in leap years or normal years? :-) As you calculate you are always going to get a slightly incorrect answer. And how long is a day in years? You say 1/365.2425. Well, yeah, averaged over a thousand years, yeah. But otherwise not.

年份没有恒定的长度。你想要闰年或正常年份的差异吗? :-)按照你的计算,你总会得到一个稍微不正确的答案。多年来的一天多长时间?你说1 / 365.2425。嗯,是的,平均超过一千年,是的。但不是。

So the question doesn't really make much sense.

所以问题并没有多大意义。

To be correct you have to do this:

要纠正你必须这样做:

from datetime import datetime
from calendar import isleap
start_date = datetime(2005,4,28,12,33)
end_date = datetime(2010,5,5,23,14)
diffyears = end_date.year - start_date.year
difference  = end_date - start_date.replace(end_date.year)
days_in_year = isleap(end_date.year) and 366 or 365
difference_in_years = diffyears + (difference.days + difference.seconds/86400.0)/days_in_year

In this case that's a difference of 0.0012322917425568528 years, or 0.662 days, considering that this is not a leap year.

在这种情况下,这是相差0.0012322917425568528年,或0.662天,考虑到这不是闰年。

(and then we are ignoring microseconds. Heh.)

(然后我们忽略了微秒。嘿。)

#4


5  

To make sense of leap years, you are almost forced to break this into two parts: an integral number of years, and a fractional part. Both need to deal with leap years, but in different ways - the integral needs to deal with a starting date of February 29, and the fractional must deal with the differing number of days in a year. You want the fractional part to increment in equal amounts until it equals 1.0 at the next anniversary date, so it should be based on the number of days in the year after the end date.

为了理解闰年,你几乎*将其分为两部分:整数年和一小部分。两者都需要处理闰年,但需要以不同的方式 - 处理2月29日开始日期的整体需求,而小数必须处理一年中不同的天数。您希望小数部分以相等的数量递增,直到它在下一个周年日等于1.0,因此它应该基于结束日期之后一年中的天数。

Do you want your date range to include 1900 or 2100? Things get a little easier if you don't.

您希望您的日期范围包括1900或2100吗?如果你不这样做,事情会变得容易些。


Edit: It has taken me a long time to reason this through. The basic problem is that calendar years are not a constant size, but you're coercing them to be constant by setting them to 1.0. Any solution you come up with is going to have anomalies because of this, and you're going to have to choose which anomalies you can live with. John Machin was right.

What's the difference between 2008-02-28 and 2009-02-28? Most people would agree that it should be exactly 1.0 years. How about the difference between 2008-03-01 and 2009-03-01? Again, most people would agree that it should be exactly 1.0 years. If you choose to represent a date as a year plus a fraction of a year based on the day, it is impossible to make both of these statements true. This is the case for your original code which assumed a day was 1/365.2425 of a year, or indeed for any code which assumes a constant fraction of a year per day, even if the size of a day accounts for the years which are leap years.

2008-02-28和2009-02-28之间有什么区别?大多数人都会同意这应该是1。0年。 2008-03-01和2009-03-01之间的区别怎么样?同样,大多数人都会同意这应该是1。0年。如果您选择将日期表示为年份加上基于当天的一年中的一小部分,则无法使这两个陈述成立。原始代码的情况就是这样,假设一天是一年的1/365.2425,或者实际上对于任何假设每天一年的常数分数的代码,即使一天的大小占据了飞跃的年份年份。

My assertion that you needed to break this down into integral years and fractional years was an attempt to get around this problem. If you treat each of the previous conditions as an integral year, all you have to do is decide on which fraction to assign to any number of days left over. The problem with this scheme is that you still can't make sense of (date2-date1)+date3, because the fraction can't be resolved back to a day with any consistency.

我断言你需要将其分解为整数年和分数年,这是试图解决这个问题。如果您将之前的每个条件都视为一个完整的年份,那么您所要做的就是决定分配给剩余天数的分数。这个方案的问题在于你仍然无法理解(date2-date1)+ date3,因为无法将分数解析回具有任何一致性的一天。

Thus I am proposing yet another encoding, based on each year containing 366 days whether it is a leap year or not. The anomalies will firstly be that there can't be a date which is exactly a year (or 2 or 3) from Feb. 29 - "Sorry Johnny, you don't get a birthday this year, there's no Feb. 29" isn't always acceptable. Second is that if you try to coerce such a number back to a date, you'll have to account for non-leap years and check for the special case of Feb. 29 and convert it, probably to Mar. 1.

因此,我提议另一种编码,基于每年包含366天,无论是否是闰年。异常将首先是2月29日之前不能有一年(或2或3)的日期 - “对不起约翰尼,你今年没有生日,没有2月29日”总是可以接受的。其次,如果你试图强迫这样的数字回到日期,你将不得不考虑非闰年并检查2月29日的特殊情况并将其转换,可能是3月1日。

from datetime import datetime
from datetime import timedelta
from calendar import isleap

size_of_day = 1. / 366.
size_of_second = size_of_day / (24. * 60. * 60.)

def date_as_float(dt):
    days_from_jan1 = dt - datetime(dt.year, 1, 1)
    if not isleap(dt.year) and days_from_jan1.days >= 31+28:
        days_from_jan1 += timedelta(1)
    return dt.year + days_from_jan1.days * size_of_day + days_from_jan1.seconds * size_of_second

start_date = datetime(2010,4,28,12,33)
end_date = datetime(2010,5,5,23,14)
difference_in_years = date_as_float(end_time) - date_as_float(start_time)

I'm not suggesting that this is the solution, because I don't think a perfect solution is possible. But it has some desirable properties:

我并不是说这是解决方案,因为我认为不可能有一个完美的解决方案。但它有一些理想的属性:

  • The difference between any dates with the same month and day and time will be an exact number of years.
  • 具有相同月份,日期和时间的任何日期之间的差异将是确切的年数。

  • Adding a difference to another date will result in a value that can be converted back into a useful date.
  • 将差异添加到另一个日期将导致可以转换回有用日期的值。

#5


2  

Just do this:

这样做:

from dateutil.relativedelta import relativedelta

myBirthday = datetime.datetime(1983,5,20,0,0,0,0)
now = datetime.datetime.now()



difference = relativedelta(now, myBirthday)
print("My years: "+str(difference.years))

#6


1  

Here's a spin off of what Kostyantyn posted in his "age2" function. It's slightly shorter/cleaner and uses the traditional/colloquial meaning of an "age" or difference in years as well:

这是Kostyantyn在他的“age2”功能中发布的内容。它略短/清洁,并使用“年龄”的传统/口语含义或年份的差异:

def ageInYears( d ):
    today = datetime.date.today()
    currentYrAnniversary = datetime.date( today.year, d.month, d.day )
    return (today.year - d.year) - (1 if today < currentYrAnniversary else 0)

#7


0  

If you mean efficient in terms of code space then no, that's about the most efficient way to do that.

如果你的意思是代码空间有效,那么不,这是最有效的方法。