2010-12-14 72 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))