关于算法:如何计算年,月,日的人的年龄?

How can I calculate the age of a person in year, month, days?

我想以相对于当前日期的年、月和日计算给定出生日期和当前日期的人的年龄。

例如:

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

任何指向某个算法的指针都将被赞赏。


首先,注意假设,比如说,你生于二月一号,准确地说是一月一号,即使你的年龄只有28岁,但比平均月长。年复一年也有变长度(由于年复一年),这意味着你的生日年龄通常不是一个准确的年复一年数。如果你想在几年/一个月/几天内准确地表达出来,请看我的其他答案。但你越想逃避这一切,就越想在二月出生,这意味着你在二月的第一天是十年,零个月,零个月,而在任何一个月的第一天,你是十年,一个月,一个月,零天。

在这种情况下,阅读。(NB:The following only works for dates in the past.)

给出出生日期,(y,m,d),当前日期,(ynow,mnow,dnow)和函数tm(),给予UNIX/EPOCH一个given date,后续行动将输出一个3元列表,给出年龄{Years,Months,days}:

1
2
3
4
5
6
7
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];

跟踪是一个等效算法,如果你不喜欢所有的流量和模式。但我认为上面的东西更好。有一件事,当它不需要时,它会呼唤tm()

ZZU1


这不是一个简单的问题,因为上面的日子(如果我们不考虑闰秒)有不容易的公式。

月可以是28天、29天、30天或31天;年可以是365天或366天。因此,当您试图计算月份和年份的完整时间单位时,就会出现问题。

这里有一篇有趣的文章,它考虑到用Java来解决您的问题的所有复杂方面。


这里的其他人有正确的想法-您只需要能够将其扩展到其他语言。许多计算机系统从一个特定的点("epoch"或"reference date")开始以秒为单位计算时间,并从那里计算出日期,并提供从秒转换为日期的方法。您应该能够以秒为单位获取当前时间,将出生日期从历元转换为秒,减去这两个值,然后将其除以一天中的秒数:

1
2
3
4
5
6
7
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
", timenow, timebirth, diff_sec, days);

这个特定代码中的bug是mktime()无法处理epoch之前的日期,在基于Unix的系统中是1970年1月1日。其他系统也会存在类似的问题,这取决于它们如何计算时间。一般的算法应该有效,你只需要知道你对特定系统的限制。


如果你愿意违背"生日那天我应该是一个整数岁"的原则,这里有一个方法可以做到这一点。请注意,由于闰年的原因,这一原则并不完全正确,因此下面的内容实际上更准确,尽管可能不太直观。如果你想知道某人实际的确切年龄,我会这样做的。

首先将出生日期和当前时间都转换为纪元时间(从时间开始的秒数,即在Unix Land中为1970秒),然后减去它们。请参阅,例如,这个问题以了解如何做到这一点:如何在Perl中将日期/时间转换为epoch时间(即unix时间——1970年以来的秒数)?你现在有了这个人的准确年龄(以秒为单位),需要把它转换成一个字符串,比如"36岁,3个月,8天"。

这是伪代码。它应该是直接删除周或小时或任何你不想要的部分…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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)); }


我接受的问题比一种编程语言宽泛。如果您使用的是C 35,您可以使用日期偏移来计算这个。

1
2
3
4
5
6
7
8
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);

你会得到你需要的信息。


由于年、月甚至天的长度可能不均匀,因此不能从减去这两个日期开始计算一个简单的持续时间。如果你想让结果与人类对待日期的方式相匹配,你就必须使用真实的日期,使用你的语言内置的功能,比你以前更了解日期。

该算法的编写方式取决于您使用的语言,因为每种语言都有一组不同的函数。我自己在Java中实现的,使用了笨拙但技术上正确的GregorianCalendar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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日期/时间库,其中确实有一个句点类,可能需要重写这个函数。


这个算法打印下一个格式->24年,8个月,2天

1
2
3
4
5
6
7
8
9
10
from dateutil.relativedelta import *
from datetime import date

def calc_age(dob):
  today = date.today()
  age = relativedelta(today, dob)
  print str(age.years)+" Years,"+str(age.months)+" Months,"+str(age.days)+" Days"

#test it
calc_age(date(1993,10,10))

我知道这是一个比特,但这里是一个简单的代码,我使用的是Python图书馆的力量(Python 3.5 for annotations,but this i s a simple code I used,with the power of the python library(Python 3.5 for annotations,but works wit

1
2
3
4
5
6
7
8
9
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)

The swift implementation of dreeves's answer.

Ps.Instead of(y,m,d)(ynow,mnow,dnow)as the inputs,I use two nsdate's,which may be more handy in real world usages.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
        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);

显然,您使用的是python:create-to-datetime对象,其中一个具有"birthday"和一个具有"now",因此请减去它们,并从生成的timedelta对象中读取年龄。

为什么要为闰年之类的事情烦恼?