2009-01-17 286 views
17

我想計算給定出生日期的人的年齡以及相對於當前日期的年,月和日的當前日期。如何計算年,月,日的人的年齡?

例如:

>>> calculate_age(2008, 01, 01) 
1 years, 0 months, 16 days 

任何指針一種算法,這是否將受到讚賞。

+0

許多語言都可以在標準庫中做到這一點。任何首選語言,或者你想要一個「純」的算法? – 2009-01-17 12:10:40

+0

看看這篇文章:http://00f.net/2007/an-age-is-not-a-duration – MatthieuP 2009-01-17 12:17:47

+0

類似於http://stackoverflow.com/questions/9/how-doi-i-計算 - someones-age-in-c – some 2009-01-17 13:39:31

回答

15

首先,請注意一個假設,例如,如果您出生於2月1日,然後在隨後的3月1日出生,即使您的年齡僅爲28天,但您的年齡恰好爲1個月大平均一個月。年份也有可變長度(由於閏年),這意味着您的生日年齡通常不是確切的整數年。如果您想要以年/月/日的形式表達您活着的確切時間,請參閱我的其他答案。但更有可能的是,你想把它恰到好處,以便在2月1日出生意味着每個2月1日你是X年,0個月和0天,並且在任何一個月的第一天你是X年,Y月,和0天。

在這種情況下,請繼續閱讀。 (注:以下內容僅適用於在過去的日期。)

生完孩子,(y,m,d),當前日期,(ynow,mnow,dnow)的日期和功能,讓unix/epoch time for a given date,以下將輸出一個3元素列表,給出年齡{年,月,日}:

t0 = y*12 + m - 1;  # total months for birthdate. 
t = ynow*12 + mnow - 1; # total months for Now. 
dm = t - t0;    # delta months.  
if(dnow >= d) return [floor(dm/12), mod(dm,12), dnow-d]; 
dm--; t--; 
return [floor(dm/12), mod(dm,12), 
     (tm({ynow,mnow,dnow}) - tm({floor(t/12), mod(t,12)+1, d}))/60/60/24]; 

以下是等效的算法,如果你不喜歡所有的地板和器官功能障礙綜合徵。但我認爲以上更好。一方面它可以避免在不需要時調用。

{yl, ml} = {ynow, mnow}; 
if(mnow < m || mnow == m && dnow < d) yl--; 
if(dnow < d) ml--; 
years = yl - y; 
months = ml + 12*(ynow - yl) - m; 
yl = ynow; 
if(ml == 0) { ml = 12; yl--; } 
days = (tm({ynow, mnow, dnow}) - tm({yl, ml, d}))/60/60/24; 
return [years, months, days]; 
-3

由於您明顯正在使用python:創建日期時間對象,其中一個使用「生日」,另一個使用「now」,將它們相減並從結果timedelta對象中讀取年齡。

爲什麼會像閏年一樣困擾?

4

這不是一個簡單的問題,因爲以上幾天(如果我們不考慮leap-seconds)有不容易的公式。

幾個月可以包含28,29,30或31天;年可以是365或366天。因此,當您嘗試計算幾個月和幾年的完整時間單位時會出現問題。

這是一個interesting article考慮到所有複雜的方面來解決你在Java中的問題。

1

有一個關於python-list的討論關於爲什麼計算年齡不是so straightforward a task

從討論中我發現this implementation(我不會複製和粘貼在這裏,因爲它不是我的)。

雖然我還沒有出現庫中的任何下降。

0

如果你願意違反「我的生日應該是整數歲」的原則,這裏有一個辦法。請注意,由於閏年,該原則並非嚴格正確,因此以下實際上更準確,但可能不太直觀。如果你想知道某人的實際確切年數,這是我會這樣做的方式。

首先將出生日期和當前時間都轉換爲紀元時間(從黎明以來的秒數,即1970年unix土地)並將其相減。例如,請看這個問題:How do I convert a date/time to epoch time (aka unix time -- seconds since 1970) in Perl?。您現在已經在幾秒鐘內確定了該人的確切年齡,需要將其轉換爲「36年,3個月,8天」之類的字符串。

這裏是僞代碼來做到這一點。它應該是簡單的去除你不想要的數週或小時或任何部分...

daysInYear = 365.2425; # Average lengh of a year in the gregorian calendar. 
         # Another candidate for len of a year is a sidereal year, 
         # 365.2564 days. cf. http://en.wikipedia.org/wiki/Year 
weeksInYear = daysInYear/7; 
daysInMonth = daysInYear/12; 
weeksInMonth = daysInMonth/7; 
sS = 1; 
mS = 60*sS; 
hS = 60*mS; 
dS = 24*hS; 
wS = 7*dS; 
oS = daysInMonth*dS; 
yS = daysInYear*dS; 

# Convert a number of seconds to years,months,weeks,days,hrs,mins,secs. 
seconds2str[secs] 
{ 
    local variables: 
    y, yQ= False, o, oQ= False, w, wQ= False, 
    d, dQ= False, h, hQ= False, m, mQ= False, s= secs; 

    if(secs<0) return "-" + seconds2str(-secs); # "+" is string concatenation. 

    y = floor(s/yS); 
    if(y>0) yQ = oQ = wQ = dQ = hQ = mQ = True; 
    s -= y * yS; 

    o = floor(s/oS); 
    if(o>0) oQ = wQ = dQ = hQ = mQ = True; 
    s -= o * oS; 

    w = floor(s/wS); 
    if(w>0) wQ = dQ = hQ = mQ = True; 
    s -= w * wS; 

    d = floor(s/dS); 
    if(d>0) dQ = hQ = mQ = True; 
    s -= d * dS; 

    h = floor(s/hS); 
    if(h>0) hQ = mQ = True; 
    s -= h * hS; 

    m = floor(s/mS); 
    if(m>0) mQ = True; 
    s -= m * mS; 

    return 
    (yQ ? y + " year" + maybeS(y) + " " : "") + 
    (oQ ? o + " month" + maybeS(o) + " " : "") + 
    (wQ ? w + " week" + maybeS(w) + " " : "") + 
    (dQ ? d + " day" + maybeS(d) + " " : "") + 
    (hQ ? dd(h) + ":" : "") + 
    (mQ ? dd(m) + ":" + dd(round(s)) + "s" : s + "s"); 
} 


# Returns an "s" if n!=1 and "" otherwise. 
maybeS(n) { return (n==1 ? "" : "s"); } 

# Double-digit: takes a number from 0-99 and returns it as a 2-character string. 
dd(n) { return (n<10 ? "0" + tostring(n) : tostring(n)); } 
1

年以來,幾個月甚至幾天可以有長度不均勻,則無法通過減去兩個日期開始得到一個簡單的持續時間。如果你想讓結果與人類對待日期的方式相匹配,你就必須使用真正的日期,使用你的語言內置的函數來比以往更好地理解日期。

如何編寫算法取決於您使用的語言,因爲每種語言都有不同的函數集。我自己的實現在Java中,使用的尷尬,但技術上是正確的GregorianCalendar:

Term difference (Date from, Date to) { 
    GregorianCalendar cal1 = new GregorianCalendar(); 
    cal1.setTimeInMillis(from.getTime()); 

    GregorianCalendar cal2 = new GregorianCalendar(); 
    cal2.setTimeInMillis(to.getTime()); 

    int years = cal2.get(Calendar.YEAR) - cal1.get(Calendar.YEAR); 
    int months = cal2.get(Calendar.MONTH) - cal1.get(Calendar.MONTH); 
    int days = cal2.get(Calendar.DAY_OF_MONTH) - cal1.get(Calendar.DAY_OF_MONTH); 
    if (days < 0) { 
     months--; 
     days += cal1.getActualMaximum(Calendar.DAY_OF_MONTH); 
    } 
    if (months < 0) { 
     years--; 
     months += 12; 
    } 
    return new Term(years, months, days); 
} 

這可能不是完美的,但它提供了人類可讀的結果。術語是我自己的類,用於存儲人類風格的時期,因爲Java的日期庫沒有。

對於未來的項目,我打算轉移到更好的Joda日期/時間庫,它有一個Period類,並且可能必須重寫此函數。

3

這裏的其他人有正確的想法 - 你只需要能夠擴展到其他語言。許多計算機系統從特定時間點(「時代」或「參考日期」)計算時間,並計算出日期,並提供從秒到日期的轉換方法。你應該能夠在數秒內獲得當前時間,出生日期從時代轉換到秒,減去兩個值,然後在一天除以秒數:

int timenow = time(0); 
struct tm birthday = { tm_mday = 16 , .tm_mon = 1 , .tm_year = 1963 }; 
int timebirth = mktime(&birthday_tm); 
int diff_sec = timenow - timebirth; 
int days = diff_sec/(24 * 60 * 60); 
printf("%d - %d = %d seconds, or %d days\n", timenow, timebirth, diff_sec, days); 

在此的錯誤特別的代碼是mktime()不能處理紀元之前的日期,這是1970年1月1日在基於Unix的系統中的日期。其他系統也會有類似的問題,這取決於他們如何計算時間。通用算法應該可行,你只需要知道你對特定系統的限制。

0

我認爲你必須指定好一點。

比方說,出生日期是2008-02-01,這一天是2009-01-31

結果是什麼?

  • 0年11個月和31天呢?
  • 0年,10個月和28 + 31 = 59天?
  • 0年,10個月和29 + 31 = 60天?
0

這是使用數學借貸規則。

 DateTime dateOfBirth = new DateTime(2000, 4, 18); 
     DateTime currentDate = DateTime.Now; 

     int ageInYears = 0; 
     int ageInMonths = 0; 
     int ageInDays = 0; 

     ageInDays = currentDate.Day - dateOfBirth.Day; 
     ageInMonths = currentDate.Month - dateOfBirth.Month; 
     ageInYears = currentDate.Year - dateOfBirth.Year; 

     if (ageInDays < 0) 
     { 
      ageInDays += DateTime.DaysInMonth(currentDate.Year, currentDate.Month); 
      ageInMonths = ageInMonths--; 

      if (ageInMonths < 0) 
      { 
       ageInMonths += 12; 
       ageInYears--; 
      } 
     } 
     if (ageInMonths < 0) 
     { 
      ageInMonths += 12; 
      ageInYears--; 
     } 

     Console.WriteLine("{0}, {1}, {2}", ageInYears, ageInMonths, ageInDays); 
0

快速執行dreeves的答案。

ps。代替(y,m,d)(ynow,mnow,dnow)作爲輸入,我使用兩個NSDate,這在現實世界中可能更方便。

extension NSDate { 

    convenience init(ageDateString:String) { 
     let dateStringFormatter = NSDateFormatter() 
     dateStringFormatter.dateFormat = "yyyy-MM-dd" 
     dateStringFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") 
     let d = dateStringFormatter.dateFromString(ageDateString)! 
     self.init(timeInterval:0, sinceDate:d) 
    } 

    func ageFrom(date: NSDate) -> (Int, Int, Int) { 
     let cal = NSCalendar.currentCalendar() 

     let y = cal.component(NSCalendarUnit.Year, fromDate: date) 
     let m = cal.component(NSCalendarUnit.Month, fromDate: date) 
     let d = cal.component(NSCalendarUnit.Day, fromDate: date) 
     let ynow = cal.component(NSCalendarUnit.Year, fromDate: self) 
     let mnow = cal.component(NSCalendarUnit.Month, fromDate: self) 
     let dnow = cal.component(NSCalendarUnit.Day, fromDate: self) 

     let t0 = y * 12 + m - 1  // total months for birthdate. 
     var t = ynow * 12 + mnow - 1; // total months for Now. 
     var dm = t - t0;    // delta months. 
     if(dnow >= d) { 
      return (Int(floor(Double(dm)/12)), dm % 12, dnow - d) 
     } 
     dm-- 
     t-- 
     return (Int(floor(Double(dm)/12)), dm % 12, Int((self.timeIntervalSince1970 - NSDate(ageDateString: "\(Int(floor(Double(t)/12)))-\(t%12 + 1)-\(d)").timeIntervalSince1970)/60/60/24)) 
    } 

} 

// sample usage 
let birthday = NSDate(ageDateString: "2012-7-8") 
print(NSDate().ageFrom(birthday)) 
1

我假設問題比只有一種編程語言更廣泛。 如果您使用的是C#,則可以使用DateTimeOffset來計算此值。

var offset = DateTimeOffset.MinValue; 
var lastDate = DateTime.Today; 
var firstDate = new DateTime(2006,11,19); 


var diff = (lastDate- firstDate); 
var resultWithOffset = DateTimeOffset.MinValue.AddDays(diff.TotalDays); 
var result = resultWithOffset.AddDays(-offset.Day).AddMonths(-offset.Month).AddYears(-offset.Year); 

result.Dayresult.Monthresult.Year將持有你所需要的信息。

0

我知道這有些過時,但這是一個簡單的代碼,我使用了Python庫的功能(Python 3.5用於註釋,但是沒有工作)。

from datetime import date, timedelta 

def age(birth: date) -> (int, int, int): 
     """ 
      Get a 3-int tuple telling the exact age based on birth date. 
     """ 
     today_age = date(1, 1, 1) + (date.today() - birth) 
     # -1 because we start from year 1 and not 0. 
     return (today_age.year - 1, today_age.month - 1, today_age.day - 1)