如何计算年,月,日的人的年龄?

时间:2009-01-17 12:05:03

标签: algorithm datetime math

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

例如:

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

任何指向执行此操作的算法的指针都将受到赞赏。

12 个答案:

答案 0 :(得分:16)

首先,请注意这样的假设:例如,如果您出生在2月1日然后在3月1日,那么您正好是1个月大,即使您的年龄仅为28天 - 小于平均一个月。年份也有可变长度(由于闰年),这意味着您的生日年龄通常不是完整的整数年。如果你想用几年/几个月/几天来表达你活着的确切时间,请参阅我的另一个答案。但更有可能你想恰到好处地捏造它,以便在2月1日出生意味着每年2月1日你是X年,0个月和0天,并且在任何一个月的第一天你是X年,Y个月,和0天。

在这种情况下,请继续阅读。 (注意:以下内容仅适用于过去的日期。)

根据出生日期(y,m,d),当前日期(ynow,mnow,dnow)以及提供unix/epoch time for a given date的函数tm(),以下内容将输出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];

如果您不喜欢所有楼层和模组,则以下是等效算法。但我认为上述情况更好。首先,它避免在不需要时调用tm()

{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];

答案 1 :(得分:4)

这不是一个简单的问题,因为上面几天(如果我们不考虑leap-seconds),就没有简单的公式。

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

这是一个interesting article,它考虑了用Java解决问题的所有复杂方面。

答案 2 :(得分: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()无法处理在基于Unix的系统中1970年1月1日之前的日期。其他系统也会存在类似的问题,具体取决于他们如何计算时间。一般算法应该有效,你只需知道你对特定系统的限制。

答案 3 :(得分:2)

如果你愿意违反“我应该在我生日那年的整数年”的原则,这是一种方法。请注意,由于闰年,该原则并不严格,因此以下实际上更准确,但可能不太直观。如果你想知道某人的实际确切年数,这就是我的方式。

首先将生日和当前时间转换为纪元时间(自时间开始以来的秒数,即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)); }

答案 4 :(得分: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);
}

这可能不完美,但它提供了人类可读的结果。 Term是我自己用于存储人类时期的类,因为Java的日期库没有。

对于未来的项目,我打算转移到更好的Joda日期/时间库,里面有一个Period类,可能需要重写这个函数。

答案 5 :(得分: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将保留您需要的信息。

答案 6 :(得分: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);

答案 7 :(得分:0)

斯威夫特实施的“诡计”回答。

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

答案 8 :(得分: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)

答案 9 :(得分:0)

此算法打印下一个格式 - &gt; 24年,8个月,2天

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

答案 10 :(得分:0)

您可以使用 PHP date_diff http://php.net/manual/en/function.date-diff.phphttp://www.php.net/manual/en/datetime.diff.php 来实现您想要的,并且您可以使用 PHP 日期格式以您想要的任何格式输出

$interval = date_diff(date_create(), date_create('2008-01-01 10:30:00')); echo $interval->format("You are %Y Year, %M Months, %d Days, %H Hours, %i Minutes, %s Seconds Old");

结果: You are 04 Year, 04 Months, 1 Days, 00 Hours, 56 Minutes, 36 Seconds Old

答案 11 :(得分:-4)

因为你显然正在使用python:创建到datetime对象,一个带有“birthday”,一个带有“now”,减去它们并从生成的timedelta对象中读取年龄。

为什么要像闰年那样烦恼?