2010-12-14 83 views
22

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

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 
+0

在何种意义上,你是希望它可以是“更有效”?更快?较少的代码? – 2010-12-14 07:58:28

+10

要明确;使用浮点常量而不是整型常量。您的最后一行需要使用'difference_in_years =(difference.days + difference.seconds/86400.0)/ 365.2425'来给出Python 2.X运行时的预期答案。 – 2010-12-14 08:09:53

+0

@John Machin好点,没有想到这一点。 – c00kiemonster 2010-12-14 08:21:20

回答

0

如果你的意思是在代码空间方面高效的则没有,这是关于最有效的方式来做到这一点。

+0

您可以预先设置一个命名常量,以一年内的秒数为单位,我猜... – 2010-12-14 09:13:27

5

更高效?不,但可能更正确。但这取决于你想成为多么正确。日期不是微不足道的事情。

年份没有固定的长度。你想要在闰年或正常年份的差异? :-)正如你计算你一直会得到一个稍微不正确的答案。多少年是多少年?你说1/365.2425。那么,是的,平均一千年来,是的。但否则不。

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

是正确的,你必须这样做:

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 

在这种情况下,这是的0.0年,或0.662天的差异,考虑到这是不是闰年。

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

+0

从不同的日历年**有不同长度的角度来看,您的答案和操作者的答案都是违反直觉的。当试图找出两天之间的差距与一天之间的差距时,这变得更加明显。 – Brian 2010-12-15 22:23:35

+0

好点,这真的说明我想说什么。通过365.2425的计算,您可以从1999-05-05到2000-05-05获得与从2000-05-05到2001-05-05不同的答案。这显然是错误的。那么你不是在数年,而是在数天。 – 2010-12-15 22:29:12

+0

如果我们想更有趣,我们可以在混合中添加时区。 – 2010-12-15 22:35:51

5

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

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


编辑:我花了很长时间来解释这一点。基本的问题是日历年不是一个固定的大小,但是通过将它们设置为1.0来强制它们保持不变。任何你想出来的解决方案都会因为这个而产生异常,你将不得不选择你可以忍受的异常。约翰Machin是对的。

2008-02-28和2009-02-28有什么区别?大多数人会同意它应该完全是1.0年。 2008-03-01和2009-03-01之间的区别怎么样?再次,大多数人会同意它应该完全是1.0年。如果您选择将日期表示为一年,再根据当天表示一年的一小部分,则不可能使这两个表述都成立。对于假设每天为1/365.2425的原始代码,或者对于假定每天的一年不变的部分的代码,即使一天中的大小占据了跳跃年年份。

我断言你需要把这个分解成整数年和分数年是为了解决这个问题。如果您将以前的每个条件都视为整数年份,则您只需决定将剩下的天数分配给哪个分数即可。这个方案的问题在于,你仍然无法理解(date2-date1)+ date3,因为这个分数不能以任何一致性解决回到一天。

因此,我正在提议另一种编码,每年的基础上包含366天,不管它是闰年还是非闰年。这个异常首先是因为从2月29日开始,不会有一年(或2或3)的日期 - “对不起,约翰尼,今年没有生日,没有2月29日的生日”,isn总是可以接受的。其次,如果您试图将这样的数字强制退回到某个日期,则必须考虑非闰年,并检查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) 

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

  • 任何日期与相同的月份,日期和时间之间的差异将是一个确切的年数。
  • 将差异添加到其他日期将导致可以转换回有用日期的值。
+0

@Mark Ransom:你想告诉OP他想要一个巴洛克式的多年花招而不是一个简单的噱头,没有他说他的应用是什么,没有你说你的用途可以放在什么地方? – 2010-12-15 23:03:33

+0

@John Machin,我没有试图告诉他他需要什么,我希望我没有遇到过这种方式。我接受了“任何建议都是值得欢迎的”的邀请,指出他的方法存在困难,无论他们是否相关。我的回答中的最后一句话措辞严厉,但我在阅读了一些评论后的最后时刻添加了它,我恳求过度匆忙。 – 2010-12-15 23:10:30

+0

@Mark Ransom:问题在于,任何一个不提及其用途的年份和方法都会遇到问题,因为没有恒定长度的年份。也许OP真的很高兴能够在几天内计算出来,并且只是将它除以365.将某些数字缩小到对他的观众来说更容易理解... – 2010-12-15 23:30:21

41

如果您想获得精确的结果,我推荐使用dateutil库。

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

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

+0

如果日期切换,这会给我带来负面结果......无论如何要纠正这种行为? – Zubo 2017-07-17 09:53:03

+0

@zubo你可以在调用relativedelta函数之前使用这个oneliner:'end_date,start_date =(end_date,start_date)if end_date> start_date else(start_date,end_date)' – 2017-08-07 14:37:57

7

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

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 
1

只是这样做:

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