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

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

Например:

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

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

Ответы [ 11 ]

16 голосов
/ 18 января 2009

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

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

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

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];
4 голосов
/ 17 января 2009

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

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

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

3 голосов
/ 18 января 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. Подобные проблемы будут существовать в других системах в зависимости от того, как они считают время. Общий алгоритм должен работать, вы просто должны знать свои ограничения в конкретных системах.

2 голосов
/ 17 января 2009

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

Сначала преобразуйте как дату рождения, так и текущее время в время эпохи (количество секунд с рассвета времени, т. Е. 1970 года в земле юникс) и вычтите их. См., Например, этот вопрос о том, как это сделать: Как преобразовать дату / время в время эпохи (также известное как unix time - секунды с 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)); }
1 голос
/ 07 июля 2016

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

1 голос
/ 18 января 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);
}

Возможно, это не идеально, но дает читабельные результаты. Термин - это мой собственный класс для хранения периодов в человеческом стиле, поскольку в библиотеках дат Java его нет.

Для будущих проектов я планирую перейти к гораздо лучшей библиотеке даты / времени Joda, в которой есть класс Period, и, вероятно, придется переписать эту функцию.

0 голосов
/ 12 июня 2018

Этот алгоритм печатает следующий формат -> 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))
0 голосов
/ 27 декабря 2016

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

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

пс. вместо (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))
0 голосов
/ 18 июня 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...