Как рассчитать чей-то возраст на Java? - PullRequest
132 голосов
/ 12 июля 2009

Я хочу вернуть возраст в годах как int в методе Java. Теперь у меня есть следующее, где getBirthDate () возвращает объект Date (с датой рождения; -)):

public int getAge() {
    long ageInMillis = new Date().getTime() - getBirthDate().getTime();

    Date age = new Date(ageInMillis);

    return age.getYear();
}

Но так как getYear () устарела, мне интересно, есть ли лучший способ сделать это? Я даже не уверен, что это работает правильно, поскольку у меня нет модульных тестов (пока).

Ответы [ 28 ]

167 голосов
/ 12 июля 2009

Check Joda , что упрощает вычисления даты / времени (Joda также является основой нового стандартного API даты / времени Java, поэтому вы изучите API, который скоро станет стандартом ).

РЕДАКТИРОВАТЬ: Java 8 имеет что-то очень похожее и стоит проверить.

например.

LocalDate birthdate = new LocalDate (1970, 1, 20);
LocalDate now = new LocalDate();
Years age = Years.yearsBetween(birthdate, now);

это так просто, как вы могли захотеть. Материал до Java 8 (как вы уже определили) несколько не интуитивен.

141 голосов
/ 12 июля 2009

JDK 8 делает это легко и элегантно:

public class AgeCalculator {

    public static int calculateAge(LocalDate birthDate, LocalDate currentDate) {
        if ((birthDate != null) && (currentDate != null)) {
            return Period.between(birthDate, currentDate).getYears();
        } else {
            return 0;
        }
    }
}

Тест JUnit для демонстрации его использования:

public class AgeCalculatorTest {

    @Test
    public void testCalculateAge_Success() {
        // setup
        LocalDate birthDate = LocalDate.of(1961, 5, 17);
        // exercise
        int actual = AgeCalculator.calculateAge(birthDate, LocalDate.of(2016, 7, 12));
        // assert
        Assert.assertEquals(55, actual);
    }
}

Все должны уже использовать JDK 8. У всех более ранних версий закончился срок их поддержки.

41 голосов
/ 12 июля 2009
Calendar now = Calendar.getInstance();
Calendar dob = Calendar.getInstance();
dob.setTime(...);
if (dob.after(now)) {
  throw new IllegalArgumentException("Can't be born in the future");
}
int year1 = now.get(Calendar.YEAR);
int year2 = dob.get(Calendar.YEAR);
int age = year1 - year2;
int month1 = now.get(Calendar.MONTH);
int month2 = dob.get(Calendar.MONTH);
if (month2 > month1) {
  age--;
} else if (month1 == month2) {
  int day1 = now.get(Calendar.DAY_OF_MONTH);
  int day2 = dob.get(Calendar.DAY_OF_MONTH);
  if (day2 > day1) {
    age--;
  }
}
// age is now correct
36 голосов
/ 02 января 2015

Современный ответ и обзор

a) Java-8 (пакет java.time)

LocalDate start = LocalDate.of(1996, 2, 29);
LocalDate end = LocalDate.of(2014, 2, 28); // use for age-calculation: LocalDate.now()
long years = ChronoUnit.YEARS.between(start, end);
System.out.println(years); // 17

Обратите внимание, что выражение LocalDate.now() неявно связано с часовым поясом системы (который часто упускается из виду пользователями). Для ясности обычно лучше использовать перегруженный метод now(ZoneId.of("Europe/Paris")) с указанием явного часового пояса (здесь, например, «Европа / Париж»). Если запрашивается системный часовой пояс, то я лично пишу LocalDate.now(ZoneId.systemDefault()), чтобы сделать связь с системным часовым поясом более ясной. Это больше усилий для написания, но облегчает чтение.

б) Joda-Time

Обратите внимание, что предлагаемое и принятое решение Joda-Time дает другой результат вычисления для дат, показанных выше (редкий случай), а именно:

LocalDate birthdate = new LocalDate(1996, 2, 29);
LocalDate now = new LocalDate(2014, 2, 28); // test, in real world without args
Years age = Years.yearsBetween(birthdate, now);
System.out.println(age.getYears()); // 18

Я считаю, что это небольшая ошибка, но команда Joda по-другому смотрит на это странное поведение и не хочет его исправлять (странно, потому что день окончания месяца меньше даты начала, поэтому год должен быть на один меньше). Смотрите также этот закрытый выпуск .

c) java.util.Calendar и т. Д.

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

d) О дате4J:

Предложенное решение простое, но иногда терпит неудачу в случае високосных лет. Просто оценка дня года не надежна.

e) Моя собственная библиотека Time4J :

Это работает аналогично Java-8-решению. Просто замените LocalDate на PlainDate и ChronoUnit.YEARS на CalendarUnit.YEARS. Однако для получения "сегодня" требуется явная ссылка на часовой пояс.

PlainDate start = PlainDate.of(1996, 2, 29);
PlainDate end = PlainDate.of(2014, 2, 28);
// use for age-calculation (today): 
// => end = SystemClock.inZonalView(EUROPE.PARIS).today();
// or in system timezone: end = SystemClock.inLocalView().today();
long years = CalendarUnit.YEARS.between(start, end);
System.out.println(years); // 17
17 голосов
/ 21 сентября 2011
/**
 * This Method is unit tested properly for very different cases , 
 * taking care of Leap Year days difference in a year, 
 * and date cases month and Year boundary cases (12/31/1980, 01/01/1980 etc)
**/

public static int getAge(Date dateOfBirth) {

    Calendar today = Calendar.getInstance();
    Calendar birthDate = Calendar.getInstance();

    int age = 0;

    birthDate.setTime(dateOfBirth);
    if (birthDate.after(today)) {
        throw new IllegalArgumentException("Can't be born in the future");
    }

    age = today.get(Calendar.YEAR) - birthDate.get(Calendar.YEAR);

    // If birth date is greater than todays date (after 2 days adjustment of leap year) then decrement age one year   
    if ( (birthDate.get(Calendar.DAY_OF_YEAR) - today.get(Calendar.DAY_OF_YEAR) > 3) ||
            (birthDate.get(Calendar.MONTH) > today.get(Calendar.MONTH ))){
        age--;

     // If birth date and todays date are of same month and birth day of month is greater than todays day of month then decrement age
    }else if ((birthDate.get(Calendar.MONTH) == today.get(Calendar.MONTH )) &&
              (birthDate.get(Calendar.DAY_OF_MONTH) > today.get(Calendar.DAY_OF_MONTH ))){
        age--;
    }

    return age;
}
11 голосов
/ 20 августа 2015

Я просто использую миллисекунды в постоянном значении года для моего преимущества:

Date now = new Date();
long timeBetween = now.getTime() - age.getTime();
double yearsBetween = timeBetween / 3.15576e+10;
int age = (int) Math.floor(yearsBetween);
10 голосов
/ 05 апреля 2011

Если вы используете GWT, вы будете ограничены использованием java.util.Date, вот метод, который принимает дату как целые числа, но все еще использует java.util.Date:

public int getAge(int year, int month, int day) {
    Date now = new Date();
    int nowMonth = now.getMonth()+1;
    int nowYear = now.getYear()+1900;
    int result = nowYear - year;

    if (month > nowMonth) {
        result--;
    }
    else if (month == nowMonth) {
        int nowDay = now.getDate();

        if (day > nowDay) {
            result--;
        }
    }
    return result;
}
4 голосов
/ 13 февраля 2011

С библиотекой date4j :

int age = today.getYear() - birthdate.getYear();
if(today.getDayOfYear() < birthdate.getDayOfYear()){
  age = age - 1; 
}
4 голосов
/ 24 сентября 2012

Правильный ответ, используя JodaTime :

public int getAge() {
    Years years = Years.yearsBetween(new LocalDate(getBirthDate()), new LocalDate());
    return years.getYears();
}

Вы можете даже сократить его до одной строки, если хотите. Я скопировал идею из ответа Брайана Агнью , но я считаю, что это более правильно, как вы видите из комментариев (и это точно отвечает на вопрос).

3 голосов
/ 21 апреля 2015

Возможно, удивительно заметить, что вам не нужно знать, сколько дней или месяцев в году или сколько дней в этих месяцах, также вам не нужно знать о високосных годах, високосных секундах или любой другой материал, использующий этот простой, 100% точный метод:

public static int age(Date birthday, Date date) {
    DateFormat formatter = new SimpleDateFormat("yyyyMMdd");
    int d1 = Integer.parseInt(formatter.format(birthday));
    int d2 = Integer.parseInt(formatter.format(date));
    int age = (d2-d1)/10000;
    return age;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...