sábado, 21 de agosto de 2010

¿Cómo trabajar fechas con Java?


Es casi seguro que alguna vez has tenido problemas para trabajar con fechas como los siguientes:
•    Sumar días
•    Restar días
•    Cuantos días hay entre una fecha y otra
•    Y todo esto teniendo en cuenta de que hay meses con 28, 29, 30 y 31 días

Esta es una gran problemática ya que no tenemos parámetros fijos con lo cual trabajarlos. Abría que escribir mucho código para hacer estos cálculos.

Si nos presentaran problemas como:
¿Cuál será la fecha en 3 días?
 Respuesta: sería obtener la fecha actual y sumarle 3 días pero si el día actual fuera el 27 de Febrero del 2010 y nos piden sumar 3 días una respuesta sería sumar 27 + 3 y listo pero eso nos daría un 30. ¿Un 30 de Febrero? Entonces creo que ya estamos visualizando la problemática.

Esto lo podríamos resolver con unos cuantos if, switch, do – while y for. Claro espero que entiendas que eso sería mucho código que hay que escribir y probar mucho a ver si funciona bien en todas las condiciones.

Java nos ofrece muchas clases utilitarias que nos ayudan a resolver problemas como estos…
Les comentaré y daré algunos consejos para manejar fechas con la clase java.util.Calendar.

La clase Calendar es una clase abstracta que provee métodos para convertir una instancia específica en el tiempo y una serie de campos pertenecientes a un calendario como:
•    YEAR
•    MONTH
•    DAY_OF_MONTH
•    HOUR …
También permite la manipulación de estos campos como para obtener la fecha de un día de la próxima semana y mucho más.

Para ver más de la definición de esta clase pueden visitar el siguiente link:
http://download.oracle.com/javase/6/docs/api/java/util/Calendar.html

Antes de empezar debemos importar la clase Calendar
import java.util.Calendar;

Ahora supongamos que queremos saber la fecha actual
Calendar calendar = Calendar.getInstance();
System.out.println("Fecha Actual: " + calendar.getTime());

Ahora si la fecha actual fuera 27 de Febrero del 2010 y nos preguntan cual es la fecha en 3 días. Por supuesto que no es un 30 de Febrero debe ser 3 de Marzo.
Para sumar tres días hacemos esto:
calendar.add(Calendar.DAY_OF_MONTH, 3);
System.out.println("Fecha Actual: " + calendar.getTime());

De igual manera para los meses y para los años.
Al tratar a los meses hay que tomar en cuenta de que el primer mes (Enero) es 0 y el último (Diciembre) es 11.
Entonces setiemos la fecha a 27 de Febrero del 2010:
calendar.set(Calendar.DATE, 27);
calendar.set(Calendar.MONTH, Calendar.FEBRUARY);
//calendar.set(Calendar.MONTH, (2-1));
//calendar.set(Calendar.MONTH, (1));
calendar.set(Calendar.YEAR, 2010);
//Para ver la fecha
System.out.println("Fecha Actual: " + calendar.getTime());

Despues de todo esto ahora les voy a demostrar como hacer uso de un Bean y también calcular la diferencia entre dos objetos Calendar.
Primero creamos un Bean con las propiedades que nos interesan al momento de calcular la diferencia entre una fecha y otra
  • Dias
  • Horas
  • Minutos
  • Segundos
  • Milisegundos
No calculo ni los meses y años ya que hay meses que tienen 28, 29, 30 y 31 días y también hay años en los que hay 365 y 366 días por lo cual no los calculo.
A continuación la clase Diferencia.

/**
 *
 * @author Billy Joel
 */
public class Diferencia {
    private long milisegundos;
    private long segundos;
    private long minutos;
    private long horas;
    private long dias;

    /**
     * @return the milisegundos
     */
    public long getMilisegundos() {
        return milisegundos;
    }

    /**
     * @param milisegundos the milisegundos to set
     */
    public void setMilisegundos(long milisegundos) {
        this.milisegundos = milisegundos;
    }

    /**
     * @return the segundos
     */
    public long getSegundos() {
        return segundos;
    }

    /**
     * @param segundos the segundos to set
     */
    public void setSegundos(long segundos) {
        this.segundos = segundos;
    }

    /**
     * @return the minutos
     */
    public long getMinutos() {
        return minutos;
    }

    /**
     * @param minutos the minutos to set
     */
    public void setMinutos(long minutos) {
        this.minutos = minutos;
    }
 

    /**
     * @return the horas
     */
    public long getHoras() {
        return horas;
    }

    /**
     * @param horas the horas to set
     */
    public void setHoras(long horas) {
        this.horas = horas;
    }

    /**
     * @return the dias
     */
    public long getDias() {
        return dias;
    }

    /**
     * @param dias the dias to set
     */
    public void setDias(long dias) {
        this.dias = dias;
    }
}

La clase anterior va a tener todas las propiedades que arriba mencionaba (dias, horas, minutos...)
Ahora la clase en donde implemento las distintas operaciones con las fechas gracias a la clase Calendar

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Calendar;
 

/**
 *
 * @author Billy Joel
 */
public class PruebaFechas {
     

    /**
     * Recibe un objeto Calendar y suma la cantidad de días que

     * se reciben como parámetros. En Java los objetos se pasan 
     * por referencia por tal motivo no es necesario devolver el
     * objeto Calendar. Basta con volver a revisar las 
     * propiedades que tiene el objeto Calendar que se envío 
     * luego de llamar a este método
     * @param calendar
     * @param dias
     */
    public void sumarDias(Calendar calendar, int dias){
        calendar.add(Calendar.DATE, dias);
    }

    /**
     * Recibe un objeto Calendar y resta la cantidad de días que

     * se reciben como parámetros. En Java los objetos se pasan 
     * por referencia por tal motivo no es necesario devolver el
     * objeto Calendar. Basta con volver a revisar las 
     * propiedades que tiene el objeto Calendar que se envío 
     * luego de llamar a este método
     * @param calendar
     * @param dias
     */
    public void restarDias(Calendar calendar, int dias){
        calendar.add(Calendar.DATE, -dias);
    }
 

    /**
     * Recibe un objeto Calendar y suma la cantidad de meses que

     * se reciben como parámetros. En Java los objetos se pasan 
     * por referencia por tal motivo no es necesario devolver el
     * objeto Calendar. Basta con volver a revisar las 
     * propiedades que tiene el objeto Calendar que se envío 
     * luego de llamar a este método
     * @param calendar
     * @param meses
     */
    public void sumarMeses(Calendar calendar, int meses){
        calendar.add(Calendar.MONTH, meses);
    }

    /**
     * Recibe un objeto Calendar y resta la cantidad de meses que

     * se reciben como parámetros. En Java los objetos se pasan 
     * por referencia por tal motivo no es necesario devolver el 
     * objeto Calendar. Basta con volver a revisar las propiedades 
     * que tiene el objeto Calendar que se envío luego de llamar
     * a este método
     * @param calendar
     * @param meses
     */
    public void restarMeses(Calendar calendar, int meses){
        calendar.add(Calendar.MONTH, -meses);
    }

    /**
     * Recibe un objeto Calendar y suma la cantidad de años que 

     * se reciben como parámetros. En Java los objetos se pasan 
     * por referencia por tal motivo no es necesario devolver 
     * el objeto Calendar. Basta con volver a revisar las 
     * propiedades que tiene el objeto Calendar que se envío 
     * luego de llamar a este método
     * @param calendar
     * @param años
     */
    public void sumarAños(Calendar calendar, int años){
        calendar.add(Calendar.YEAR, años);
    }

    /**
     * Recibe un objeto Calendar y resta la cantidad de años que 

     * se reciben como parámetros. En Java los objetos se pasan 
     * por referencia por tal motivo no es necesario devolver el 
     * objeto Calendar. Basta con volver a revisar las 
     * propiedades que tiene el objeto Calendar que se envío 
     * luego de llamar a este método
     * @param calendar
     * @param años
     */
    public void restarAños(Calendar calendar, int años){
        calendar.add(Calendar.YEAR, -años);
    }

    /**
     * Este método devulve un bean llamado Diferencia el cual 

     * puede contiene datos sobre las diferencias que hay entre 
     * dos objeto Calendar.
     * (Dias. Horas, Minutos. Segundos y Milisegundos).
     * @param fecha1
     * @param fecha2
     * @return Diferencia
     */
    public Diferencia obtenerDiferencia(Calendar fecha1, Calendar fecha2){
        Diferencia diferencias = new Diferencia();
        BigDecimal resto;
        long dif = fecha1.getTimeInMillis() - fecha2.getTimeInMillis();
        long milisegundos;
        long segundos;
        long minutos;
        long horas;
        long dias;

        resto = BigDecimal.valueOf(dif/1000.00);
        segundos = obtenerEnteros(resto);
        milisegundos = obtenerDecimales(resto);

        resto = BigDecimal.valueOf(segundos / 60.00);
        minutos = obtenerEnteros(resto);
        segundos = (long) ((resto.toBigInteger().longValue() - minutos) * 60);

        resto = BigDecimal.valueOf(minutos / 60.00);
        horas = obtenerEnteros(resto);
        minutos = (long) ((resto.toBigInteger().longValue() - horas) * 60);

        resto = BigDecimal.valueOf(horas / 24.00);
        dias = obtenerEnteros(resto);
        horas = (long) ((resto.toBigInteger().longValue() - dias) * 24);

        diferencias.setMilisegundos(milisegundos);
        diferencias.setSegundos(segundos);
        diferencias.setMinutos(minutos);
        diferencias.setHoras(horas);
        diferencias.setDias(dias);


        return diferencias;
    }

    /**
     * Obtiene la parte entera de un número decimal
     * Ejemplo: 1234.56789 devlvería 1234
     * @param numero BigDecimal
     * @return long
     */
    private long obtenerEnteros(BigDecimal numero){
        String s = numero.toPlainString();
        if(s.contains("" + new DecimalFormat().getDecimalFormatSymbols().getDecimalSeparator()))
            return Long.parseLong(s.substring(0, s.indexOf(new DecimalFormat().getDecimalFormatSymbols().getDecimalSeparator())));
        else
            return Long.parseLong(s);
    }

    /**
     * Obtiene la parte decimal de un númerro decimal
     * Ejemplo: 1234.56789 devolvería 56789
     * @param numero
     * @return long
     */
    private long obtenerDecimales(BigDecimal numero){
        String s = numero.toPlainString();
        if(s.contains("" + new DecimalFormat().getDecimalFormatSymbols().getDecimalSeparator()))
            return Long.parseLong(s.substring(s.indexOf(new DecimalFormat().getDecimalFormatSymbols().getDecimalSeparator()) + 1));
        else
            return 0;
    }

    public static void main(String [] args){
        //Esta es una muestra de como implentar ésta clase
        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        c2.set(2003, Calendar.FEBRUARY, 26);
        PruebaFechas pr = new PruebaFechas();
        Diferencia dif = pr.obtenerDiferencia(c1, c2);
        System.out.println("La diferencia es: " + dif.getDias() + " dias, " + dif.getHoras() + " horas, " + dif.getMinutos() + " minutos, " + dif.getSegundos() + " segundos, " + dif.getMilisegundos() + " milisegundos");
    }
}

Este articulo lo he hecho basado en mi experiencia y de seguro que a ustedes les va a servir mucho también.

1 comentario: