Calculation of age in Java

Asked

Viewed 517 times

3

I have a code that should calculate the age, but in my test I can’t catch the day nor the month of Data and yet age comes wrong due to this. On the test returns 32 years, while it should be 31.

public Data(Integer day, Integer month, Integer year) {
    if(month < 1 && month > 12) {
        System.out.println("Informe um mes valido.");
    }
    if (day < 1 && day > 31) {
        System.out.println("Tem que ser de 1 a 31!!!");
    }
    if (year > 2020) {
        System.out.println("Ano invalido");
    }
    if (validarData(day, month, year)) {
        this.day = day;
        this.month = month;
        this.year = year;
    }
}

public boolean validarData (Integer dayV, Integer monthV, Integer yearV) {
    boolean dayOk = false;
    if (monthV == 4 || monthV == 6 || monthV == 9 || monthV == 11) {
        if (dayV <= 30) {
            dayOk = true;
        } else {
            dayOk = false;
        }
    }

    if (monthV == 1 || monthV == 3 || monthV == 5 || monthV == 7 || monthV == 8 || monthV == 10 || monthV == 12) {
        if (dayV <= 31) {
            dayOk = true;
        } else {
            dayOk = false;
        }
    }
    if (monthV == 2) {
        if (yearV % 4 == 0) {
            if (dayV <= 29) {
                dayOk = true;
            } else {
                dayOk = false;
            }
        } else {
            if (dayV <= 28) {
                dayOk = true;
            } else {
                dayOk = false;
            }
        }
    }
    if (dayOk) {
        return true;
    } else {
        return false;
    }
}

public static int calculoIdade(Data data) {
    Calendar cHoje= Calendar.getInstance();
    return cHoje.get(Calendar.YEAR) - data.year;
}

public static void main(String[] args) {

    Data data = new Data(03, 11, 1988);
    System.out.println("Idade: " + calculoIdade(data));

}
  • Nothing may be less than 1 and greater than 31. Perhaps you wish to do if (day < 1 || day > 31) {, ditto {if(month < 1 || month > 12) {, or in place of and.

1 answer

4


In the calculation, you are only taking into account the year, but you should also check if the person’s birthday has passed (if it has not passed, you have to subtract 1 from age). Thus:

public static int calculoIdade(Data dataNascimento) {
    Calendar hoje = Calendar.getInstance();
    int idade = hoje.get(Calendar.YEAR) - dataNascimento.year;
    // se ainda não chegou o aniversário, diminui 1 ano
    int mesAtual = hoje.get(Calendar.MONTH) + 1;
    if ((mesAtual == dataNascimento.month && hoje.get(Calendar.DAY_OF_MONTH) < dataNascimento.day)
        || mesAtual < dataNascimento.month) {
        idade--;
    }
    return idade;
}

One annoying detail is that in class Calendar the months are indexed to zero (January is zero, February is 1, etc.), so you have to add 1 to get the correct value of the month (since your class Data seems to use the correct values: January 1, February 2, etc).

Then I see if the person has not yet birthday this year. IE:

  • whether it is in the same month and the birthday has not yet arrived, or
  • if the birthday month has not yet arrived

If any of the above conditions occur, it is because this year’s birthday has not yet occurred, and in that case should decrease 1 of age.


There are other details to consider.

In the constructor, if any value is invalid, you print the respective error message, but continue creating the date anyway. But if the value is invalid, the Data nor should it be created. Instead, she should make an exception (read more about this here, here and here).

In addition, it is possible to simplify the logic of checking the day based on the month. It is also worth remembering that a leap year is one that is divisible by 4, except for if divisible by 100 (unless also divisible by 400).

Finally, a suggestion for the constructor and the validation of the date would be:

public Data(int day, int month, int year) {
    validarData(day, month, year);
    this.day = day;
    this.month = month;
    this.year = year;
}

private void validarData(int day, int month, int year) {
    if (month < 1 || month > 12) {
        throw new IllegalArgumentException("Informe um mês válido.");
    }
    if (year > 2020) {
        throw new IllegalArgumentException("Ano inválido");
    }
    // primeiro calcula a quantidade de dias do mês
    int qtdDias;
    if (month == 2) {
        // ano bissexto, divisível por 4 (mas se for divisível por 100, só é bissexto se for divisível por 400)
        if (year % 4 == 0 && ((year % 100) != 0 || (year % 400) == 0)) {
            qtdDias = 29;
        } else {
            qtdDias = 28;
        }
    } else if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
        qtdDias = 31;
    } else {
        qtdDias = 30;
    }
    // verifica se o dia está no intervalo válido
    if (day < 1 || day > qtdDias) {
        throw new IllegalArgumentException("Dia tem que ser de 1 a " + qtdDias);
    }
}

I left the method validarData as private, Apparently, it’s only used by the class itself to validate. If any value is invalid, it throws the exception and the instance is not created (since it makes no sense to validate, inform that the value is wrong and create the instance anyway).

Note that the day and month condition uses the operator || ("or") and not && ("and"). That’s because there’s no way that the month is both smaller than 1 and larger than 12 (either one or the other or none, it just doesn’t have to be both at the same time), so use &&, would never get into the if.

I also changed the fields of Integer for int, because I don’t think there’s any reason to use the Wrappers - read more about here.


Use a Date API

If it’s just an exercise, it’s okay to try to implement a class that represents a date. But if it’s for code in production, use what you already have.

From Java 8 you can use the API java.time. To calculate age, it would look like this:

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

public static int calculoIdade(int dia, int mes, int ano) {
    return (int) ChronoUnit.YEARS.between(LocalDate.of(ano, mes, dia), LocalDate.now());
}

The difference is that the method between returns a long, but if you know that the value will always be less than 231-1 (just over 2 billion), can do the cast for int hassle-free.

And the class itself LocalDate already checks the values of the day, month and year, if it is leap, etc, and launches a exception if any of them is invalid. You would only need to add the condition of checking if year is higher than 2020, which is specific to your code.

For versions prior to JDK 8, you can use JDK 8 itself Calendar. Or, for JDK 6 and 7 there is the Threeten Backport, an excellent backport of java.time. Most of the features of Java 8 are present, but instead of the classes being in the package java.time, they stay in the package org.threeten.bp (apart from this, the code above would look the same).


February 29

It is worth remembering that there is a corner case. If the person was born, for example, on 29 February 2020, how old will he be on 28 February 2021?

The above codes consider that the age will be zero, as her birthday has not yet arrived. But as in the year 2021, February only has 28 days, so only from March 1 will be considered that the age is 1.

If you want to consider that in a non-leap year, February 28th is the "birthday", you will have to put this rule in your methods.

In your class Data, would be:

private static boolean bissexto(int ano) {
    return ano % 4 == 0 && ((ano % 100) != 0 || (ano % 400) == 0);
}

public static int calculoIdade(Data dataNascimento) {
    Calendar hoje = Calendar.getInstance();
    int idade = hoje.get(Calendar.YEAR) - dataNascimento.year;
    // se ainda não chegou o aniversário, diminui 1 ano
    int mesAtual = hoje.get(Calendar.MONTH) + 1;
    int diaAtual = hoje.get(Calendar.DAY_OF_MONTH);
    if ((mesAtual == dataNascimento.month && diaAtual < dataNascimento.day) || mesAtual < dataNascimento.month) {
        // mas se nasceu em 29/02 e hoje é 28/02 (e o ano não é bissexto), não subtrai
        if (!(diaAtual == 28 && mesAtual == 2
              && dataNascimento.day == 29 && dataNascimento.month == 2
              && !bissexto(hoje.get(Calendar.YEAR))))
            idade--;
    }
    return idade;
}

Note that I have devised an auxiliary method to verify that the year is leap. But with the java.time you can use the class Year, which has a ready method to check this:

public static int calculoIdade(int dia, int mes, int ano) {
    LocalDate hoje = LocalDate.now();
    int idade = (int) ChronoUnit.YEARS.between(LocalDate.of(ano, mes, dia), hoje);
    // se nasceu em 29/02 e hoje é 28/02 (e o ano não é bissexto), soma 1 na idade
    if (dia == 29 && mes == 2 && hoje.getDayOfMonth() == 28
        && hoje.getMonthValue() == 2 && !Year.isLeap(hoje.getYear()))
        idade++;
    return idade;
}

For a more complete discussion of leap year verification, see this question.

  • You gave me a java lesson, which I don’t remember having such a detailed explanation. The one in the Calendar class that indexes January as Zero, I really didn’t know; But if you can still explain me only the question of Ifs that Cvoce told me it doesn’t vailda 2 things at the same time, if Month < 1 && Month > 12. in Python I use a lot if this is the same as that and the other so does such a thing.

  • 1

    @Thiagothyroids Are 2 different conditions. If you test if (day >= 1 && day <= 31), checks if the day is at once greater than or equal to 1 and less than or equal to 31, and there works both in Java qto in Python. But vc was doing if (day < 1 && day > 31) (if the day is less than 1 and greater than 31), and there it will never be true, for no number is at the same time less than 1 and greater than 31.

  • 1

    @Tiagotfdias Something else: If the answer solved your problem, you can accept it, see here how and why to do it. It is not mandatory, but it is a good practice of the site, to indicate to future visitors that it solved the problem. Don’t forget that you can also vote in response, if it has found it useful.

  • 1

    A tah, now I understand the logic of ifs that I was doing. vdd. I thank you very much, I will vote yes, and if it helps other people it makes me well too.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.