¿Cómo restar X días de una fecha usando el calendario Java?


180

¿Alguien sabe una manera simple de usar el calendario Java para restar X días de una fecha?

No he podido encontrar ninguna función que me permita restar directamente X días de una fecha en Java. ¿Alguien puede señalarme en la dirección correcta?


Para su información, las viejas clases problemáticas de fecha y hora como java.util.Calendarahora son heredadas , suplantadas por las clases java.time . Ver Tutorial de Oracle .
Basil Bourque

Respuestas:


309

Tomado de los documentos aquí :

Agrega o resta la cantidad de tiempo especificada al campo de calendario dado, según las reglas del calendario. Por ejemplo, para restar 5 días de la hora actual del calendario, puede lograrlo llamando:

Calendar calendar = Calendar.getInstance(); // this would default to now
calendar.add(Calendar.DAY_OF_MONTH, -5).

1
Solo tenga cuidado al hacer esto, ya que no siempre funciona como espera.
carson

1
Esta respuesta tiene muchos votos a favor, pero ¿es seguro de usar? o esto es mejor: stackoverflow.com/a/10796111/948268
Kuldeep Jain

44
No lo usaría Calendar.DAY_OF_MONTHen este caso ya que no manejará el rollo entre meses como le gustaría. Usar en su Calendar.DAY_OF_YEARlugar
algoritmos el

44
Esto debería ser seguro, aparentemente cuando lo agregas, no importa si es DAY_OF_MONTH o DAY_OF_YEAR stackoverflow.com/q/14065198/32453
rogerdpack

@carson ¿Cuáles son algunos de los problemas con este enfoque?
tatmanblue

38

Podrías usar el addmétodo y pasarle un número negativo. Sin embargo, también podría escribir un método más simple que no use la Calendarclase como el siguiente

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

Esto obtiene el valor de fecha y hora de la fecha (milisegundos desde la época) y agrega la cantidad adecuada de milisegundos. Podría pasar un número entero negativo para el parámetro días para restar. Esto sería más simple que la solución de calendario "adecuada":

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() );
}

Tenga en cuenta que ambas soluciones cambian el Dateobjeto pasado como parámetro en lugar de devolver uno completamente nuevo Date. Cualquiera de las funciones se puede cambiar fácilmente para hacerlo de la otra manera si se desea.


3
No crea que .setTime y .add son métodos estáticos de Calendar. Debería estar utilizando la variable de instancia c.
Edward

@ Edward: estás en lo correcto, gracias por señalar mi error, he arreglado el código para que ahora funcione correctamente.
Eli Courtwright

3
Tenga cuidado con la primera manera, por ejemplo, cuando se trata de días de leapyear puede fallar: stackoverflow.com/a/1006388/32453
rogerdpack

Para su información, las viejas clases problemáticas de fecha y hora como java.util.Calendarahora son heredadas , suplantadas por las clases java.time . Ver Tutorial de Oracle .
Basil Bourque

36

La respuesta de Anson funcionará bien para el caso simple, pero si va a hacer cálculos de fechas más complejos, le recomiendo que revise Joda Time . Hará tu vida mucho más fácil.

FYI en Joda Time que podrías hacer

DateTime dt = new DateTime();
DateTime fiveDaysEarlier = dt.minusDays(5);

1
@ShahzadImam, echa un vistazo a DateTimeFormat . Le permitirá convertir instancias de DateTime a cadenas utilizando formatos arbitrarios. Es muy similar a la clase java.text.DateFormat.
Mike Deck

1
A partir de Java 8, considere usar java.time docs.oracle.com/javase/8/docs/api/java/time/…
toidiu el

Para su información, el proyecto Joda-Time ahora está en modo de mantenimiento , y el equipo aconseja migrar a las clases java.time . Ver Tutorial de Oracle .
Basil Bourque

19

tl; dr

LocalDate.now().minusDays( 10 )

Es mejor especificar la zona horaria.

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

Detalles

Las antiguas clases de fecha y hora incluidas con las primeras versiones de Java, como java.util.Date/.Calendar , han demostrado ser problemáticas, confusas y defectuosas. Evítales.

java.time

Java 8 y posterior suplanta esas clases antiguas con el nuevo marco java.time. Ver tutorial . Definido por JSR 310 , inspirado en Joda-Time , y ampliado por el proyecto ThreeTen-Extra . El proyecto ThreeTen-Backport transfiere las clases a Java 6 y 7; el proyecto ThreeTenABP para Android.

La pregunta es vaga, no está clara si solicita una fecha solamente o una fecha y hora.

LocalDate

Para una fecha solamente, sin hora del día, use la LocalDateclase. Tenga en cuenta que una zona horaria es crucial para determinar una fecha como "hoy".

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

ZonedDateTime

Si se refería a una fecha y hora, use la Instantclase para obtener un momento en la línea de tiempo en UTC . Desde allí, ajústese a una zona horaria para obtener un ZonedDateTimeobjeto.

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

Tabla de tipos de fecha y hora en Java, tanto modernos como heredados.


Sobre java.time

El marco java.time está integrado en Java 8 y versiones posteriores. Estas clases suplantar la vieja problemáticos heredados clases de fecha y hora como java.util.Date, Calendar, y SimpleDateFormat.

El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a las clases java.time .

Para obtener más información, consulte el Tutorial de Oracle . Y busque Stack Overflow para obtener muchos ejemplos y explicaciones. La especificación es JSR 310 .

Puede intercambiar objetos java.time directamente con su base de datos. Utilice un controlador JDBC compatible con JDBC 4.2 o posterior. No hay necesidad de cuerdas, no hay necesidad dejava.sql.* clases.

¿Dónde obtener las clases java.time?

El proyecto ThreeTen-Extra extiende java.time con clases adicionales. Este proyecto es un campo de pruebas para posibles adiciones futuras a java.time. Usted puede encontrar algunas clases útiles aquí, como Interval, YearWeek, YearQuarter, y más .


Esta es una mejora mucho mejor que las otras opciones (también es mucho más nueva y hace uso de nuevos métodos). Si está visitando este problema ahora, este es el camino a seguir.
Shourya Bansal


5

En lugar de escribir el mío addDayscomo lo sugiere Eli, preferiría usarlo DateUtilsdesde Apache . Es útil especialmente cuando tienes que usarlo en múltiples lugares en tu proyecto.

La API dice:

addDays(Date date, int amount)

Agrega una cantidad de días a una fecha que devuelve un nuevo objeto.

Tenga en cuenta que devuelve un nuevo Dateobjeto y no realiza cambios en el anterior.


2

Se puede hacer fácilmente de la siguiente manera

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());

Esta respuesta no es aconsejable, ya que utiliza clases terribles de fecha y hora que ahora son heredadas, suplantadas por las clases java.time. Además, este código ignora la cuestión crucial de la zona horaria, asumiendo ingenuamente días de 24 horas. Otro problema es usar una clase de fecha y hora para resolver un problema de solo fecha. Usar LocalDatees mucho más simple y más apropiado.
Basil Bourque

1

Alguien recomendó Joda Time, así que he estado usando esta clase CalendarDate http://calendardate.sourceforge.net

Es un proyecto un tanto competitivo para Joda Time, pero mucho más básico con solo 2 clases. Es muy útil y funcionó muy bien para lo que necesitaba, ya que no quería usar un paquete más grande que mi proyecto. A diferencia de las contrapartes de Java, su unidad más pequeña es el día, por lo que es realmente una fecha (no tenerla en milisegundos o algo así). Una vez que cree la fecha, todo lo que debe hacer para restar es algo como myDay.addDays (-5) para retroceder 5 días. Puedes usarlo para encontrar el día de la semana y cosas así. Otro ejemplo:

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

Y:

//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
}

1

Creo que se puede lograr una forma limpia y agradable de restar o agregar cualquier unidad de tiempo (meses, días, horas, minutos, segundos, ...) usando la clase java.time.Instant .

Ejemplo para restar 5 días de la hora actual y obtener el resultado como Fecha:

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

Otro ejemplo para restar 1 hora y agregar 15 minutos:

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

Si necesita más precisión, la instancia mide hasta nanosegundos. Métodos que manipulan la parte de nanosegundos:

minusNano()
plusNano()
getNano()

Además, tenga en cuenta que esa Fecha no es tan precisa como Instant.


1
O dependiendo del gusto, un poco más corto:Date.from(Instant.now().minus(5, ChronoUnit.DAYS))
Ole VV

1
¡Agradable! Tienes razón. De hecho, actualicé la respuesta para usar esto y también mostrar un uso de la clase java.time.Duration, que en este caso también es muy potente.
Yordan Boev

0

La segunda solución de Eli Courtwright está mal, debería ser:

Calendar c = Calendar.getInstance();
c.setTime(date);
c.add(Calendar.DATE, -days);
date.setTime(c.getTime().getTime());

2
El nombre de la función en la solución de Eli es addDays; Su solución es correcta. Si desea restar días de la fecha, pasa un valor negativo para days.
Thomas Upton

Bueno, eso es algo que el programador tiene que especificar al crear la función, ¿no? : P
user178973
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.