Как я могу рассчитать возраст человека в году, месяце, днях?

Я хочу рассчитать возраст человека с учетом даты рождения и текущей даты в годах, месяцах и днях относительно текущей даты.

Например:

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

Любой указатель на алгоритм, который это делает, будет оценен.


person Baishampayan Ghose    schedule 17.01.2009    source источник
comment
Многие языки могут делать это в своих стандартных библиотеках. Любой предпочтительный язык или чистый алгоритм?   -  person Jonas Elfström    schedule 17.01.2009
comment
Прочтите эту статью: 00f.net/2007/an-age -is-not-a-duration   -  person MatthieuP    schedule 17.01.2009
comment
В python-list обсуждается, почему вычисление возраста не такая простая задача. Из этого обсуждения я нашел эту реализацию (которую я выиграл не копировать и вставлять сюда, потому что это не мое). Однако я не обнаружил ни капли в библиотеках.   -  person Aaron Maenpaa    schedule 17.01.2009
comment
Подобно http://stackoverflow.com/questions/9/how-do-i-calculate-someones-age-in-c   -  person some    schedule 17.01.2009
comment
Я думаю, что вам нужно сформулировать требования немного лучше. Допустим, дата рождения - 01.02.2008, а день - 31.01.2009. Каков результат? - 0 лет, 11 месяцев и 31 день? - 0 лет, 10 месяцев и 28 + 31 = 59 дней? - 0 лет, 10 месяцев и 29 + 31 = 60 дней?   -  person John Nilsson    schedule 18.01.2009
comment
Подумайте только, насколько увлекательными будут эти вычисления, когда мы начнем учитывать разные временные масштабы от путешествий с релятивистскими скоростями.   -  person Shannon Nelson    schedule 19.01.2009
comment
@MatthieuP: Что там написано? Мертвая ссылка ...   -  person Svish    schedule 08.12.2010
comment
@Svish: Вот обновленная ссылка: 00f. net / 2007/06/04 / an-age-is-not-a-duration   -  person MatthieuP    schedule 12.12.2010
comment
Для ответа на снимок посетите stackoverflow.com/questions/19632069/   -  person Imran Ali Khan    schedule 14.06.2014


Ответы (12)


Во-первых, обратите внимание на предположение, что если, например, вы родились 1 февраля, то следующего 1 марта вам будет ровно 1 месяц, даже если ваш возраст в днях составляет всего 28 дней - меньше, чем продолжительность среднего месяца. . Годы также имеют переменную длину (из-за високосных лет), что означает, что ваш возраст в день вашего рождения обычно не является точным целым числом лет. Если вы хотите выразить точное количество времени, которое вы прожили в годах / месяцах / днях, посмотрите мой другой ответ. Но, скорее всего, вы захотите обмануть это как раз так, чтобы рождение 1 февраля означало, что каждое 1 февраля вам будет X лет, 0 месяцев и 0 дней, а 1-го числа любого месяца вам будет X лет, Y месяцев, и 0 дней.

В таком случае читайте дальше. (NB: следующее работает только для дат в прошлом.)

Учитывая дату рождения (y,m,d), текущую дату (ynow,mnow,dnow) и функцию tm(), которая дает время unix / epoch для для заданной даты следующее выведет список из трех элементов с указанием возраста в виде {лет, месяцев, дней}:

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];
person dreeves    schedule 18.01.2009
comment
Кстати, я считаю, что это правильно для людей, родившихся 29 февраля. А именно, им на один день меньше целого числа лет 28 февраля и на один день больше, чем целое число лет 1 марта, независимо от того, было 29 фев. - person dreeves; 19.01.2009
comment
На самом деле я использую этот код (модифицированный для C #) в написанном мной приложении WP7. Сегодня, должно быть, мне повезло с тестированием, потому что, когда я сравнивал 31 января 2012 года с 20 октября 2011 года, самую последнюю часть, где вы вызываете tm () с d в качестве последнего параметра, он задыхался, потому что пытаюсь построить дату 31.09.2011, которой явно не существует. Я решил это, установив d на количество дней в месяце для года и месяца в этой точке (yl, ml), если день был больше, чем количество дней в месяце. Не так сложно кодировать, если язык не поддерживает это. DateTime.DaysInMonth (год, месяц) в C #. - person Chris Doggett; 21.10.2011
comment
Спасибо за сообщение об ошибке! Думаю, я тестировал это с более умной версией tm (), где tm (2011,9,31) совпадает с tm (2011,10,1). В вашей версии месяц после 31 августа - 30 сентября; в моей версии это 1 октября. Вероятно, это зависит от приложения, которое имеет больше смысла (и, вероятно, в основном на самом деле не имеет большого значения). - person dreeves; 25.10.2011

Это непростой вопрос, поскольку указанные выше дни (если мы не принимаем во внимание дополнительные секунды) нет простых формул.

Месяцы могут состоять из 28, 29, 30 или 31 дня; года могут быть 365 или 366 дней. Поэтому проблемы возникают, когда вы пытаетесь рассчитать полные единицы времени за месяцы и годы.

Вот интересная статья, в которой учтите все сложные аспекты решения вашего вопроса на Java.

person Fernando Miguélez    schedule 17.01.2009
comment
Это не просто сверхурочные часы. Дни и недели по-прежнему однозначны. Настоящая уловка состоит в том, чтобы определить продолжительность года (и месяц, являющийся 12-й частью года, также зависит от этого определения). Но все остальное однозначно определяется в секундах. Смотрите мой ответ ниже. - person dreeves; 18.01.2009
comment
Я обновлял его от часов к дням, вы правы. Я не упоминаю недели, потому что это обычно нежелательный способ измерения периодов (и в примере ghoseb также не относится к неделям) - person Fernando Miguélez; 18.01.2009

У других здесь правильная идея - вам просто нужно иметь возможность распространить его на другие языки. Многие компьютерные системы считают время в секундах от определенной точки («эпоха» или «контрольная дата») и вычисляют дату оттуда, а также предлагают методы для преобразования секунд в даты. Вы должны иметь возможность получить текущее время в секундах, преобразовать дату рождения в секунды из эпохи, вычесть два значения, а затем разделить их на количество секунд в дне:

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 () не может работать с датами, предшествующими эпохе, то есть 1 января 1970 года в системах на основе Unix. Подобные проблемы будут существовать и в других системах в зависимости от того, как они считают время. Общий алгоритм должен работать, вам просто нужно знать свои пределы для конкретных систем.

person Shannon Nelson    schedule 18.01.2009
comment
кстати, я голосовал за это, но вам, вероятно, следовало использовать time_t вместо int. mktime () - отличная функция. - person dicroce; 18.01.2009
comment
Да, time_t, возможно, было бы лучше. Это пример забавы C: вы можете проделывать всевозможные забавные трюки с именами типов. По крайней мере, я скомпилировал и запустил код перед тем, как опубликовать :-). - person Shannon Nelson; 19.01.2009
comment
... за исключением того, что в сутках нет фиксированного количества секунд. На границе летнего времени дни на час длиннее или короче. И это во всем мире разное. - person Marcus Downing; 30.01.2009

Вот способ сделать это, если вы готовы нарушить принцип «В день рождения мне должно быть целое число лет». Обратите внимание, что этот принцип не совсем верен из-за високосных лет, поэтому следующий текст на самом деле более точен, хотя, возможно, менее интуитивно понятен. Если вы хотите знать, сколько лет кому-то на самом деле, я бы сделал это так.

Сначала преобразуйте дату рождения и текущее время во время эпохи (количество секунд с начала времен, т. Е. 1970 в мире Unix) и вычтите их. См., Например, этот вопрос, чтобы узнать, как это сделать: Как преобразовать дату / время время эпохи (также известное как время unix - секунды с 1970 года) в 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)); }
person dreeves    schedule 17.01.2009
comment
К сожалению, если не учитывать високосные годы и переход на летнее время, часто получается выходной. - person Michael Borgwardt; 17.01.2009
comment
Спасибо, Майкл! Я добавил предостережение об этом к ответу. Вы можете возразить, что ответ, который дает целое число лет в день рождения человека, - это то, что часто будет выходным днем! Так что я думаю, что этот ответ тоже имеет значение, в зависимости от конкретного приложения. - person dreeves; 17.01.2009

Поскольку годы, месяцы и даже дни могут иметь разную длину, вы не можете начать с вычитания двух дат, чтобы получить простую продолжительность. Если вы хотите, чтобы результат соответствовал тому, как люди относятся к датам, вам вместо этого придется работать с реальными датами, используя встроенные в ваш язык функции, которые понимают даты лучше, чем вы когда-либо.

То, как написан этот алгоритм, зависит от используемого вами языка, потому что каждый язык имеет свой набор функций. Моя собственная реализация на 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, и, вероятно, придется переписать эту функцию.

person Marcus Downing    schedule 18.01.2009

Вот это использование математического правила заимствования.

        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);
person Rajeshwaran S P    schedule 18.06.2009

Быстрая реализация ответа dreeves.

пс. вместо (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))
person wye    schedule 24.09.2015

Я предполагаю, что вопросы шире, чем только один язык программирования. Если вы используете 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.Day, result.Month, result.Year будут содержать нужную вам информацию.

person Morten Herman Langkjær    schedule 07.07.2016
comment
Я тестировал это, но это не работает. Имеет смещение - person user2991288; 02.07.2019

Я знаю, что он немного устарел, но вот простой код, который я использовал, с мощью библиотеки 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)
person informaticienzero    schedule 27.12.2016

Этот алгоритм выводит следующий формат - ›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))
person Capriatto    schedule 12.06.2018

Вы можете использовать PHP date_diff http://php.net/manual/en/function.date-diff.php или http://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

person Anupam Hayat Shawon    schedule 18.07.2021

Поскольку вы, очевидно, используете python: Create to datetime объекты, один с «днем рождения», а другой с «сейчас», вычтите их и прочтите возраст из полученного объекта timedelta.

Зачем возиться с такими вещами, как високосные годы?

person Martin Thurau    schedule 17.01.2009
comment
Я использую Python, но мне действительно нужен общий алгоритм, который можно реализовать на любом языке, который я использую. - person Baishampayan Ghose; 17.01.2009