Why when creating a Calendar, do I need to subtract 1 of the month?

Asked

Viewed 363 times

3

When I create a product and call the constructor passing the parameters, including day, month and year, I’m not able to understand why I need to subtract 1 month in the method set, note below:

import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;

public class Produto {

    // Formatador de data
    private static final DateFormat df = DateFormat.getDateInstance();

    // Nome do produto
    private String nome;

    // Pedo do produto
    private double peso;

    // Data de validade
    private Date dataValidade;


    // Construtor. Recebe nome, peso e data de validade
    public Produto(String nome, double peso, int dia, int mes, int ano) {
        this.nome = nome;
        this.peso = peso;

        // Cria um Calendar e seta os componentes da data
        Calendar c = Calendar.getInstance();
        c.set(ano, mes - 1, dia);

        this.dataValidade = c.getTime();

    }

    public String getNome() {
        return nome;
    }

    public double getPeso() {
        return peso;
    }

    public Date getDataValidade() {
        return dataValidade;
    }

    // Obtém a data formatada como uma String no padrao curto
    public String getFormattedDataValidate() {
        return df.format(dataValidade);
    }

}

1 answer

3


According to the documentation of Calendar, the value of the month to be passed to the method set is indexed at zero. That is, January is 0, February is 1, etc.

For this reason, 1 is subtracted from the reported value, because the correct value of the month is probably passed there (1 for January, 2 for February, etc.). But by passing this amount to Calendar, have to subtract 1 so that it generates the correct date.

This is a little confusing, but that’s how the class Calendar works. That’s one of the reasons (it’s not the main one, but it’s definitely one that contributes) why this API is so criticized.


java.time

If using Java >= 8 and your code does not depend on the use of Date and Calendar, an alternative is to use the API java.time. This API solves many of the old API problems.

If you only want the date with day, month and year (and do not need the time nor the Timezone), can use a java.time.LocalDate:

public class Produto {

    private LocalDate dataValidade;

    public Produto(String nome, double peso, int dia, int mes, int ano) {
        this.dataValidade = LocalDate.of(ano, mes, dia);
        ...
    }
}

This API has several improvements regarding Date and Calendar. The simplest - and at the same time one of the best - is that now the months are no longer indexed to zero and therefore have the correct values (January is 1, February is 2, etc). It is no longer necessary to subtract 1, as it was with Calendar.

Already to format the date, use a java.time.format.DateTimeFormatter. In your case, you were using DateFormat.getDateInstance(), which uses the standard default locale of the JVM. To obtain the equivalent of java.time, use a java.time.format.DateTimeFormatterBuilder, along with a java.time.format.FormatStyle:

public class Produto {

    private LocalDate dataValidade;

    private static final DateTimeFormatter FMT = new DateTimeFormatterBuilder()
        .appendLocalized(FormatStyle.MEDIUM, null).toFormatter();

    public String getFormattedDataValidade() {
        return FMT.format(dataValidade);
    }
}

If you don’t already use Java 8, you can use Threeten Backport, which has the same classes already mentioned (LocalDate, DateTimeFormatter, etc) and it basically works the same way. The difference is that they are in the package org.threeten.bp (instead of java.time). The backport is compatible with JDK 6 and 7.


In case you are still attached to Java 5 and want to use something better than Date and Calendar, an alternative is the Joda-Time (although this is a project considered "terminated", since on your site there’s a warning indicating this and recommending the use of java.time)

Joda-Time has classes with names similar to java.time, although it is not 100% equal. Classes are in the package org.joda.time, follows a similar example to the previous code:

import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

...
LocalDate date = new LocalDate(ano, mes, dia);
DateTimeFormatter fmt = DateTimeFormat.mediumDate();
System.out.println(fmt.print(date));

More about the java.time can be seen in this question.

  • Hello. Actually, I just remembered that, excellent explanation, thank you!

Browser other questions tagged

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