Buenas,
Ya estoy de nuevo otra vez. Siento el retraso, no es que haya abandonado el blog
, he estado super liado con cursos en la empresa y la verdad que no me ha permitido dedicarme mucho al blog.
Hoy vamos hablar de nuevo sobre las fechas en Java, es un tema tan amplio que pienso que aun me quedan múchisimos posts para escribir sobre el tema.
Veamos, este Post en concreto, tiene mucho que ver con el anterior que escribí sobre fechas: como utilizar correctamente las fechas en Java. ¿Por qué? Porque es realmente importante utilizar las herramientas que proporciona Java para las fechas, ya que a veces cuyas herramientas tienen en cuenta factores que nosotros no podemos controlar, como ejemplo podéis leer el Post del link anterior y lo comprobaréis con vuestros ojos.
Hoy voy a explicar una cosa que me he dejado en todos los Posts de Java que he escrito anteriormente, y que omití (perdonadme):
¿Qué herramienta utilizar para restar dos fechas en Java? ¿Qué utilizar para obtener la diferencia entre dos fechas en java?
Yo recomiendo, plenamente, utilizar el GregorianCalendar. Es lo que comentaba antes, hay que utilizar las herramientas de Java destinadas a la solución/manejo del tema a tratar, en este caso las Fechas.
¿Por qué no utilizar la clase Date? Si miráis minuciosamente la API de la clase Date, podréis comprobar que muchos de los métodos están siendo Deprecated y SUN están dando más relevancia las clases Calendar, Gregorian Calendar, etc.
Con eso no quiero decir que funcione mejor o peor una Clase u otra, pero desde mi punto de vista siempre hay que utilizar las clases idóneas. Reitero, MI PUNTO DE VISTA.
¿Cómo restar dos fechas en Java? ¿Cómo obtener la diferencia entre dos fechas en Java?
Necesitamos los siguientes datos para calcular la resta:
- Tener dos objetos GregorianCalendar o Date (ya sea obtenidos con string, con Calendar, etc…)
- Efectuar operaciones matemáticas simples (como mucho una multiplicación) sin que intervengan los milisegundos
A este punto voy a dividirlo en dos subpuntos: restar fechas del mismo año o restar fechas de distinto año. Empecemos:
Restar dos Fechas en Java que son del mismo año
/*CREAMOS EL GREGORIAN CALENDAR DE LAS DOS FECHAS*/
GregorianCalendar t1 = new GregorianCalendar(2009,9,23);
GregorianCalendar t2 = new GregorianCalendar(2009,9,28);
/*RESTAR FECHAS*/
int dias = t2.get(Calendar.DAY_OF_YEAR) - t1.get(Calendar.DAY_OF_YEAR);
/*IMPRESION POR PANTALLA*/
System.out.println("Valor de días es: " + dias);
Están fácil como esto, RESTAR y PUNTO. Salida por pantalla es:
Valor de días es: 5
Se puede dar el caso de que necesitemos crear los dos objetos GregorianCalendar dados dos objetos Date. El código quedaría así:
/* CREAMOS LOS OBJETOS GREGORIAN CALENDAR PARA EFECTUAR LA RESTA */
GregorianCalendar date1 = new GregorianCalendar();
date1.setTime(dateIni); //dateIni es el objeto Date
GregorianCalendar date2 = new GregorianCalendar();
date2.setTime(dateFin); //dateFin es el objeto Date
¿Qué problemas puede conllevar esto?
Pues el problema está cuando cogemos dos fechas de distinto año, el problema que puede darse es que el número no sea el correcto o el que esperamos. Veamos como es la salida por pantalla de la resta de las fechas: 2009/12/25 y 2010/01/02 (teóricamente deberían ser 8 días):
Valor de días es: -357 //( si le restásemos 365 días que tiene el año daría
Restar dos Fechas en Java de diferente año
Gracias al comentario anterior, y buscando en san Google se me ocurrió la brillante idea (que buscando, buscando, también lo encontré en un foro inglés
) de hacer lo siguiente:
/*CREAMOS EL GREGORIAN CALENDAR DE LAS DOS FECHAS*/
GregorianCalendar date1 = new GregorianCalendar(2009,11,25);
GregorianCalendar date2 = new GregorianCalendar(2010,0,2);
/* COMPROBAMOS SI ESTAMOS EN EL MISMO ANYO */
if (date1.get(Calendar.YEAR) == date2.get(Calendar.YEAR)) {
System.out.println( "Valor de Resta simple: " +String.valueOf(date2.get(Calendar.DAY_OF_YEAR) - date1.get(Calendar.DAY_OF_YEAR)));
} else {
/* SI ESTAMOS EN DISTINTO ANYO COMPROBAMOS QUE EL ANYO DEL DATEINI NO SEA BISIESTO
* SI ES BISIESTO SON 366 DIAS EL ANYO
* SINO SON 365
*/
int diasAnyo = date1.isLeapYear(date1.get(Calendar.YEAR)) ? 366 : 365;
/* CALCULAMOS EL RANGO DE ANYOS */
int rangoAnyos = date2.get(Calendar.YEAR) - date1.get(Calendar.YEAR);
/* CALCULAMOS EL RANGO DE DIAS QUE HAY */
int rango = (rangoAnyos * diasAnyo) + (date2.get(Calendar.DAY_OF_YEAR) - date1.get(Calendar.DAY_OF_YEAR));
}
System.out.println("Valor de rangoDias:" + (date2.get(Calendar.DAY_OF_YEAR) - date1.get(Calendar.DAY_OF_YEAR)));
System.out.println("Valor de rangoAnyos: " + nAnyos);
System.out.println("Valor de rango: " + rango);
El resultado importante es el de rango, resultado por pantalla es:
Valor de rangoAnyos: -357
Valor de nAnyos: 1
Valor de diasCorrectos: 8
Fijáos que he tenido que controlar la opción del AÑO BISIESTO… que también se tiene que controlar. Si volvéis a mirar el código lo he solucionado con esta simple operacion:
int diasAnyo = date1.isLeapYear(date1.get(Calendar.YEAR)) ? 366 : 365;
También deseo remarcar que he realizado un IF para controlar si la resta se hace con fechas del mismo año o si son diferentes años
Cómo restar con la clase Date (MI OPINIÓN INCORRECTO)
Utilizando los milisegundos es problemático… desde mi punto de vista, INCORRECTO. Problemas con cambios de hora,…
Date actual = new Date();
Date fecha =null;
String dateFrom = "20091023";
SimpleDateFormat day= new SimpleDateFormat("yyyyMMdd");
try {
fecha = day.parse(dateFrom);
} catch (ParseException ex) {
ex.getMessage();
ex.printStackTrace();
}
long diferencia= ( fecha.getTime() - actual.getTime() );
System.out.println("Valor de diferencia: " + diferencia/(1000*60*60*24));
¿Es una opción viable? SÍ, menos cuando hay cambios de hora por el medio. Debemos ir con precaución y repito: mejor utilizar las clases Java preparadas para el problema en cuestión.
Espero que os haya servido de algo, al menos para solucionar algún problema o ayudaros a entender un poco más las fechas en Java.
ACTUALIZACIÓN
Actualizo este post por el mero echo que aNieto2K me ha sugerido que pusiera estadísticas
. Lo sé Andrés, tengo que currarmelo más jejeje.
Para calcular las estadísticas he realizado una clase Main que contiene dos métodos, uno con la opción de GregorianCalendar y la otra con la opción del tipo Date. Para evaluar correctamente los dos metodos, he tenido que hacer que a ambos métodos se le pasen los objetos en concreto. Veamos el código:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author bibigeek
*/
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
/*EJEMPLO RESTAR 2 FECHAS EN JAVA*/
String dateFrom = "20091020";
String dateTo = "20091028";
SimpleDateFormat day= new SimpleDateFormat("yyyyMMdd");
Date ini = null;
try {
ini = day.parse(dateFrom);
} catch (ParseException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
Date fin = null;
try {
fin = day.parse(dateTo);
} catch (ParseException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
GregorianCalendar date1 = new GregorianCalendar();
date1.setTime(ini);
GregorianCalendar date2 = new GregorianCalendar();
date2.setTime(fin);
long inicioTime = System.currentTimeMillis();
for(int i=0; i<10000;i++){
String fecha = Main.restaFechas(date1, date2);
}
long finalTime = System.currentTimeMillis();
System.out.println(finalTime-inicioTime);
inicioTime = System.currentTimeMillis();
for(int i=0; i<10000;i++){
String fecha = Main.restaFechas2(ini, fin);
}
finalTime = System.currentTimeMillis();
System.out.println(finalTime-inicioTime);
}
public static String restaFechas(GregorianCalendar date1, GregorianCalendar date2) {
if (date1.get(Calendar.YEAR) == date2.get(Calendar.YEAR)) {
return String.valueOf(date2.get(Calendar.DAY_OF_YEAR) - date1.get(Calendar.DAY_OF_YEAR));
} else {
/* SI ESTAMOS EN DISTINTO ANYO COMPROBAMOS QUE EL ANYO DEL DATEINI NO SEA BISIESTO
* SI ES BISIESTO SON 366 DIAS EL ANYO
* SINO SON 365
*/
int diasAnyo = date1.isLeapYear(date1.get(Calendar.YEAR)) ? 366 : 365;
/* CALCULAMOS EL RANGO DE ANYOS */
int rangoAnyos = date2.get(Calendar.YEAR) - date1.get(Calendar.YEAR);
/* CALCULAMOS EL RANGO DE DIAS QUE HAY */
int rango = (rangoAnyos * diasAnyo) + (date2.get(Calendar.DAY_OF_YEAR) - date1.get(Calendar.DAY_OF_YEAR));
return String.valueOf(rango);
}
}
public static String restaFechas2 (Date ini, Date fin){
long diferencia= (fin.getTime() - ini.getTime());
return String.valueOf(diferencia/(1000*60*60*24));
}
}
Después de este código, veamos los resultados de su ejecución mediante gráficas. Lo he ejectuado 10 veces (y si comprobáis, cada vez que se está ejecutando lo hace en 10000 iteraciones):


Tal vez se me esta escapando algo pero lo del año bisiesto no es preciso, porque si la resta de años da digamos 5 y el DATEINI es bisiesto me daria 5*366 dias + (date2.get(Calen….., lo que es equivalente a afirmar que todos esos años fueron bisiestos
Hola ssabrewolf,
Tienes toda la razón no es lo suficientemente preciso para el ejemplo que estás buscando mmmm muy. Si lo es para el ejemplo que he expuesto.
Pensaré una solución más correcta.
Muchas gracias ssabrewolf
Estoy hasta la polla
Nechete que problema tienes????
Que os parece esta función?
No hay que preocuparse de si hay varios bisiestos o no.
public Integer restaDias(GregorianCalendar f1, GregorianCalendar f2){
Integer resta = 0;
Integer tmp = 0;
while(f1.compareTo(f2)<0){
tmp = f2.get(GregorianCalendar.DAY_OF_YEAR)-f1.get(GregorianCalendar.DAY_OF_YEAR);
f2.add(GregorianCalendar.DAY_OF_YEAR,-tmp);
resta += tmp;
if(f1.compareTo(f2)<0){
tmp = f2.get(GregorianCalendar.DAY_OF_YEAR)+1;
f2.add(GregorianCalendar.DAY_OF_YEAR,-tmp);
resta += tmp;
}
}
return resta;
}