Como subtrair X dias de uma data usando o calendário Java?

180

Alguém conhece uma maneira simples de usar o calendário Java para subtrair X dias de uma data?

Não consegui encontrar nenhuma função que me permita subtrair diretamente X dias de uma data em Java. Alguém pode me indicar a direção certa?

fmsf
fonte
Para sua informação, as antigas classes problemáticas de data e hora, como java.util.Calendaragora são herdadas , substituídas pelas classes java.time . Consulte o Tutorial da Oracle .
Basil Bourque

Respostas:

309

Retirado dos documentos aqui :

Adiciona ou subtrai a quantidade de tempo especificada ao campo de calendário especificado, com base nas regras do calendário. Por exemplo, para subtrair 5 dias da hora atual do calendário, você pode obtê-lo ligando para:

Calendar calendar = Calendar.getInstance(); // this would default to now
calendar.add(Calendar.DAY_OF_MONTH, -5).
Anson Smith
fonte
1
Apenas tome cuidado, pois nem sempre rola como você espera.
Carson
1
Essas respostas têm muitos votos positivos, mas é seguro usá-lo? ou isso é melhor: stackoverflow.com/a/10796111/948268
Kuldeep Jain
44
Você não usaria Calendar.DAY_OF_MONTHneste caso, uma vez que ele não lidará com a rolagem entre os meses como você gostaria. Use em Calendar.DAY_OF_YEARvez disso
algoritmos
4
Este deve ser seguro, aparentemente quando você está adicionando não importa se é DAY_OF_MONTH ou DAY_OF_YEAR stackoverflow.com/q/14065198/32453
rogerdpack
@ carson Quais são alguns dos problemas dessa abordagem?
tatmanblue
38

Você pode usar o addmétodo e passar um número negativo. No entanto, você também pode escrever um método mais simples que não use a Calendarclasse, como a seguinte

public static void addDays(Date d, int days)
{
    d.setTime( d.getTime() + (long)days*1000*60*60*24 );
}

Isso obtém o valor do carimbo de data / hora da data (milissegundos desde a época) e adiciona o número adequado de milissegundos. Você pode passar um número inteiro negativo para o parâmetro days para fazer a subtração. Isso seria mais simples que a solução de calendário "adequada":

public static void addDays(Date d, int days)
{
    Calendar c = Calendar.getInstance();
    c.setTime(d);
    c.add(Calendar.DATE, days);
    d.setTime( c.getTime().getTime() );
}

Observe que essas duas soluções alteram o Dateobjeto passado como parâmetro em vez de retornar um completamente novo Date. Qualquer uma das funções pode ser facilmente alterada para fazer o contrário, se desejado.

Eli Courtwright
fonte
3
Não acredite .setTime e .add são métodos estáticos do Calendário. Você deve estar usando a variável de instância c.
Edward
@ Edward: você está correto, obrigado por apontar meu erro, eu corrigi o código para que agora funcione corretamente.
Eli Courtwright
3
Tenha cuidado com a primeira forma, por exemplo, quando se trata de dias leapyear ele pode falhar: stackoverflow.com/a/1006388/32453
rogerdpack
Para sua informação, as antigas classes problemáticas de data e hora, como java.util.Calendaragora são herdadas , substituídas pelas classes java.time . Consulte o Tutorial da Oracle .
Basil Bourque
36

A resposta de Anson funcionará bem no caso simples, mas se você quiser fazer cálculos de datas mais complexos, recomendo verificar o Joda Time . Isso tornará sua vida muito mais fácil.

FYI em Joda Time você poderia fazer

DateTime dt = new DateTime();
DateTime fiveDaysEarlier = dt.minusDays(5);
Mike Deck
fonte
1
@ ShahzadImam, confira DateTimeFormat . Isso permitirá que você converta instâncias do DateTime em seqüências de caracteres usando formatos arbitrários. É muito semelhante à classe java.text.DateFormat.
30612 Mike Deck Deck
1
No Java 8, considere o uso de java.time docs.oracle.com/javase/8/docs/api/java/time/…
toidiu
Para sua informação, o projeto Joda-Time agora está no modo de manutenção , com a equipe aconselhando a migração para as classes java.time . Consulte o Tutorial da Oracle .
Basil Bourque
19

tl; dr

LocalDate.now().minusDays( 10 )

Melhor especificar o fuso horário.

LocalDate.now( ZoneId.of( "America/Montreal" ) ).minusDays( 10 )

Detalhes

As antigas classes de data e hora incluídas em versões anteriores do Java, como java.util.Date/ .Calendar, provaram ser problemáticas, confusas e com falhas. Evite-os.

java.time

O Java 8 e versões posteriores substituem essas classes antigas com a nova estrutura java.time. Veja o Tutorial . Definido pelo JSR 310 , inspirado no Joda-Time e estendido pelo projeto ThreeTen-Extra . O projeto ThreeTen-Backport faz o back-port das classes para Java 6 e 7; o projeto ThreeTenABP para Android.

A pergunta é vaga, não está claro se ela pede apenas uma data ou uma data e hora.

LocalDate

Para apenas uma data, sem hora do dia, use o LocalDate classe. Observe que um fuso horário é crucial para determinar uma data como "hoje".

LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) );
LocalDate tenDaysAgo = today.minusDays( 10 );

ZonedDateTime

Se você quis dizer uma data e hora, use a Instantclasse para obter um momento na linha do tempo no UTC . A partir daí, ajuste para um fuso horário para obter um ZonedDateTimeobjeto.

Instant now = Instant.now();  // UTC.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
ZonedDateTime tenDaysAgo = zdt.minusDays( 10 );

Tabela de tipos de data e hora em Java, modernos e herdados.


Sobre java.time

A estrutura java.time é integrada ao Java 8 e posterior. Essas classes suplantar os velhos problemáticos legados aulas de data e hora, como java.util.Date, Calendar, e SimpleDateFormat.

O projeto Joda-Time , agora em modo de manutenção , aconselha a migração para as classes java.time .

Para saber mais, consulte o Tutorial do Oracle . E procure Stack Overflow para muitos exemplos e explicações. A especificação é JSR 310 .

Você pode trocar objetos java.time diretamente com seu banco de dados. Use um driver JDBC compatível com JDBC 4.2 ou posterior. Não há necessidade de strings, não há necessidade de java.sql.*classes.

Onde obter as classes java.time?

O projeto ThreeTen-Extra estende java.time com classes adicionais. Este projeto é um campo de prova para possíveis adições futuras ao java.time. Você pode encontrar algumas classes úteis aqui, como Interval, YearWeek, YearQuarter, e mais .

Basil Bourque
fonte
Essa é uma melhoria muito melhor do que as outras opções (também é muito mais recente e utiliza novos métodos). Se você está visitando este problema agora, este é o caminho a percorrer.
Shourya Bansal 18/04
5

Em vez de escrever meus próprios, addDaysconforme sugerido por Eli, eu preferiria usar DateUtilsdo Apache . É útil, especialmente quando você precisa usá-lo em vários locais do seu projeto.

A API diz:

addDays(Date date, int amount)

Adiciona vários dias a uma data retornando um novo objeto.

Observe que ele retorna um novo Dateobjeto e não faz alterações no próprio anterior.

Risav Karna
fonte
2

Isso pode ser feito facilmente pelo seguinte

Calendar calendar = Calendar.getInstance();
        // from current time
        long curTimeInMills = new Date().getTime();
        long timeInMills = curTimeInMills - 5 * (24*60*60*1000);    // `enter code here`subtract like 5 days
        calendar.setTimeInMillis(timeInMills);
        System.out.println(calendar.getTime());

        // from specific time like (08 05 2015)
        calendar.set(Calendar.DAY_OF_MONTH, 8);
        calendar.set(Calendar.MONTH, (5-1));
        calendar.set(Calendar.YEAR, 2015);
        timeInMills = calendar.getTimeInMillis() - 5 * (24*60*60*1000);
        calendar.setTimeInMillis(timeInMills);
        System.out.println(calendar.getTime());
rab
fonte
Esta resposta é desaconselhada, usando classes terríveis de data e hora antigas que agora são herdadas, substituídas pelas classes java.time. Além disso, esse código ignora a questão crucial do fuso horário, assumindo ingenuamente dias de 24 horas. Outro problema é usar uma classe de data e hora para resolver um problema somente de data. Usar LocalDateé muito mais simples e mais apropriado.
Basil Bourque
1

Alguém recomendou o Joda Time então - eu tenho usado essa classe CalendarDate http://calendardate.sourceforge.net

É um projeto um tanto competitivo para o Joda Time, mas muito mais básico em apenas 2 aulas. É muito útil e funcionou muito bem para o que eu precisava, pois não queria usar um pacote maior que o meu projeto. Ao contrário dos equivalentes Java, sua menor unidade é o dia, portanto é realmente uma data (não diminuindo em milissegundos ou algo assim). Depois de criar a data, tudo o que você faz para subtrair é algo como myDay.addDays (-5) para voltar 5 dias. Você pode usá-lo para encontrar o dia da semana e coisas assim. Outro exemplo:

CalendarDate someDay = new CalendarDate(2011, 10, 27);
CalendarDate someLaterDay = today.addDays(77);

E:

//print 4 previous days of the week and today
String dayLabel = "";
CalendarDate today = new CalendarDate(TimeZone.getDefault());
CalendarDateFormat cdf = new CalendarDateFormat("EEE");//day of the week like "Mon"
CalendarDate currDay = today.addDays(-4);
while(!currDay.isAfter(today)) {
    dayLabel = cdf.format(currDay);
    if (currDay.equals(today))
        dayLabel = "Today";//print "Today" instead of the weekday name
    System.out.println(dayLabel);
    currDay = currDay.addDays(1);//go to next day
}
mikato
fonte
1

Eu acredito que uma maneira limpa e agradável de executar subtração ou adição de qualquer unidade de tempo (meses, dias, horas, minutos, segundos, ...) possa ser alcançada usando o java.time.Instant classe .

Exemplo para subtrair 5 dias da hora atual e obter o resultado como Data:

new Date(Instant.now().minus(5, ChronoUnit.DAYS).toEpochMilli());

Outro exemplo para subtrair 1 hora e adicionar 15 minutos:

Date.from(Instant.now().minus(Duration.ofHours(1)).plus(Duration.ofMinutes(15)));

Se você precisar de mais precisão, a Instância mede até nanossegundos. Métodos que manipulam parte de nanossegundo:

minusNano()
plusNano()
getNano()

Além disso, lembre-se de que a data não é tão precisa quanto o instantâneo.

Yordan Boev
fonte
1
Ou, dependendo do gosto, um pouco mais curto:Date.from(Instant.now().minus(5, ChronoUnit.DAYS))
Ole VV
1
Agradável! Você está certo. Na verdade, atualizei a resposta para usar isso e também mostro o uso da classe java.time.Duration, que neste caso também é muito potente.
Yordan Boev 11/12/19
0

Eli Courtwright segunda solução está errada, deve ser:

Calendar c = Calendar.getInstance();
c.setTime(date);
c.add(Calendar.DATE, -days);
date.setTime(c.getTime().getTime());
user178973
fonte
2
O nome da função na solução de Eli é addDays; sua solução está correta. Se você deseja subtrair dias da data, passa um valor negativo para days.
Thomas Upton
Bem, isso é algo que o programador deve especificar ao criar a função, não? : P
user178973