Autor Tema: Calcular diferencia de días entre dos fechas contando años bisiestos struct c++  (Leído 20078 veces)

candidatopardo

  • Visitante
Este es un ejercicio propuesto de una página web que no creo deba/pueda compartir por aquí. Pero es un ejercicio muy divertido.

Usas struct, campos de bits, y algunas funciones para por ejemplo, calcular la diferencia en días entre dos fechas. Para el curioso que quiera ver el trabajo, aquí se lo presento:

Soy bastante novicio en esto de la programación, pero aun así he intentado explicar las cosas con comentarios, y todo eso. Si os ha servido de algo, por favor, comentad.

¡Suerte!

Código: [Seleccionar]
#include <iostream>

using namespace std;

const char MESES[][11] = {{"INVALIDO"},{"Enero"},{"Febrero"},{"Marzo"},{"Abril"},{"Mayo"},{"Junio"},{"Julio"},{"Agosto"},{"Septiembre"},{"Octubre"},{"Noviembre"},{"Diciembre"}};
const int DIASMES[] = {0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

struct fecha{
    unsigned short day:5; // 0-31 [Usamos 1-31]
    unsigned short month:4; // 0-15 [Usamos 1-12]
    unsigned short year:7; // 0-127 [Usamos 0-127] + 1960

    void mostrarFecha(); // MUESTRA LA FECHA
    bool validarFecha(); // TRUE = SI LOS DIAS ESTAN DENTRO DE LOS RANGOS CONTANDO CON EL BISIESTO
    bool bisiesto(); // TRUE = AÑO BISIESTO, FALSE = AÑO NO BISIESTO
};

int compararFechas(fecha fecha1, fecha fecha2);
int Diferencia(fecha fecha1, fecha fecha2);

int main()
{
    fecha fecha1;
    fecha1.day = 16;
    fecha1.month = 1;
    fecha1.year = 0;

    fecha fecha2;
    fecha2.day = 29;
    fecha2.month = 3;
    fecha2.year = 4;

    cout << "FECHA 1: ";
    fecha1.mostrarFecha();
    cout << "." << endl;

    cout << "FECHA 2: ";
    fecha2.mostrarFecha();
    cout << "." << endl;

    cout << "LA DIFERENCIA DE DIAS ENTRE LAS DOS FECHAS ES: " << Diferencia(fecha1,fecha2) << " dia/s." << endl;

    return 0;
}

void fecha::mostrarFecha(){
    cout << day << " de " << MESES[month] << " de " << year+1960;
}


bool fecha::validarFecha(){

    if(day <= 0) return false;

    if(month == 2 && bisiesto()){
        if(day > 29) return false;
        return true;
    }
    if(day > DIASMES[month]) return false;
    return true;
}

bool fecha::bisiesto(){
    if( (year+1960) % 4 == 0 && ((year+1960) % 100 != 0 || (year+1960) % 400 == 0) ){
        return true;
    }
    return false;
}

int compararFechas(fecha fecha1, fecha fecha2){
    if(fecha1.year > fecha2.year) return -1;
    if(fecha1.year < fecha2.year) return 1;

    // AÑOS IGUALES
    if(fecha1.month > fecha2.month) return -1;
    if(fecha1.month < fecha2.month) return 1;

    // AÑOS IGUALES - MESES IGUALES
    if(fecha1.day > fecha2.day) return -1;
    if(fecha1.day < fecha2.day) return 1;

    // LAS FECHAS SON IGUALES
    return 0;
}

 int Diferencia(fecha fecha1, fecha fecha2){
    // VARIABLES AUXILIARES
    fecha fechaMayor, fechaMenor;
    int dias = 0;

    switch(compararFechas(fecha1,fecha2)){
    case -1:
        fechaMayor = fecha1;
        fechaMenor = fecha2;
        break;
    case 1:
        fechaMayor = fecha2;
        fechaMenor = fecha1;
        break;
    case 0: // LAS FECHAS SON IGUALES
        return 0;
    }

    if(fechaMenor.year < fechaMayor.year){ // El año de la fecha menor, es menor que el de la fecha mayor
        if(fechaMenor.month <= 2 && fechaMenor.bisiesto()){
            if(fechaMenor.month == 1){
                dias += DIASMES[fechaMenor.month] - fechaMenor.day;
                dias++; // PASAMOS AL DIA 1 DE FEBRERO
                // ACTUALIZAMOS LOS CAMBIOS
                fechaMenor.month = 2;
                fechaMenor.day = 1;
            }

            dias += 29 - fechaMenor.day;
            dias++; // PASAMOS AL DIA 1 DE MARZO
            // ACTUALIZAMOS LOS CAMBIOS
            fechaMenor.month = 3;
            fechaMenor.day = 1;
        }
        // LLEGADOS A ESTE PUNTO, SABEMOS QUE O NO ES BISIESTO, O SI ES BISIESTO YA HEMOS CONTADO CON ELLO
        // PASAMOS DE AÑO
        for(int m = fechaMenor.month; m <= 12; m++){
            dias += DIASMES[m];
        }
        // ACTUALIZAMOS LOS CAMBIOS
        fechaMenor.year++;
        fechaMenor.month = 1;
        fechaMenor.day = 1;

        // HEMOS PASADO AL AÑO SIGUIENTE, PERO ES POSIBLE QUE AUN FALTEN AÑOS HASTA IGUALAR
        // EL AÑO DE LA FECHA MENOR CON EL DE LA FECHA MAYOR
        // ENTONCES, SUMAMOS 1 A 1 ESOS AÑOS. SUMANDO 365 SI NO ES BISIESTO, Y 366 SI ES BISIESTO

        for(int y = fechaMenor.year; y < fechaMayor.year; y++){
            // SUMAMOS AÑOS
            if(fechaMenor.bisiesto()) dias += 366;
            else dias += 365;
            // APLICAMOS LOS CAMBIOS
            fechaMenor.year++;
        }

        // HEMOS IGUALADO LOS AÑOS, AHORA SOLO FALTA ACERCAR LOS MESES Y LOS DIAS HASTA DONDE TENGAMOS QUE ACERCARNOS
        // OJITO CON LOS BISIESTOS. EN ESTE CASO COMPROBAMOS LA FECHA MAYOR.
        // SI LA FECHA MAYOR ES BISIESTO Y EL MES ES SUPERIOR A FEBRERO, ACERCAMOS LA FECHA MENOR A 1 DE MARZO.
        // SINO, SUMAMOS DÍAS TRANQUILAMENTE

        if(fechaMayor.bisiesto() && fechaMayor.month > 2){
            dias += DIASMES[1]; // PASAMOS A FEBRERO
            dias += 29; // PASAMOS A MARZO (CONTANDO EL DIA BISIESTO)
            fechaMenor.month = 3;
            // fechaMenor.day = 1 // NO ES NECESARIO YA QUE EN LOS PASOS ANTERIORES YA DEFINIMOS QUE EL DÍA IBA A SER 1
        }

        // SI ES QUE ES BISIESTO Y EL MES DE LA FECHA MAYOR ES DE MARZO EN ADELANTE, ESTAMOS EN 1 DE MARZO.
        // Y SINO, ESTAMOS EN 1 DE ENERO
        // EN AMBOS CASOS ESTAMOS EN EL MISMO AÑO
        // PODEMOS CONTAR DIAS TRANQUILAMENTE HASTA IGUALAR LOS MESES SI FUERA NECESARIO (NO ES NECESARIO EN CASO DE QUE EL MES DE LA FECHA MAYOR SEA MARZO)

        for(int m = fechaMenor.month; m < fechaMayor.month; m++){
            dias += DIASMES[m]; // PASAMOS DE MESES
            // APLICAMOS LOS CAMBIOS
            fechaMenor.month++;
        }

        // ESTAMOS EN EL MISMO AÑO Y EN EL MISMO MES, AHORA, SOLO CALCULAMOS LA DIFERENCIA DE DIAS. SI, ¡LO TENEMOS!

        dias += fechaMayor.day - fechaMenor.day; // IGUALAMOS LAS FECHAS (POR FIN)
        // APLICAMOS LOS CAMBIOS (AUNQUE YA NO ES NECESARIO)
        fechaMenor.day = fechaMayor.day;

        return dias; // ¡BIEEN!
    }

    // LOS AÑOS SON IGUALES, ASI QUE ESO DE PASAR AL AÑO SIGUIENTE, Y LUEGO IGUALAR LOS AÑOS, NOS LO PODEMOS SALTAR
    // PUEDEN PASAR DOS COSAS

    // 1 - EL MES DE LA FECHA MENOR ES MENOR QUE EL DE LA FECHA MAYOR
    // 2 - EL MES DE LA FECHA MENOR ES IGUAL QUE EL DE LA FECHA MAYOR

    // EN EL CASO 1 TENDREMOS QUE IGUALAR LOS MESES
    // EN EL CASO 2 SOLO SOLTAMOS LA RESTA DE DIAS
    if(fechaMenor.month < fechaMayor.month){
        if(fechaMayor.month > 2 && fechaMayor.bisiesto() && fechaMenor.month <= 2){
            dias += (DIASMES[fechaMenor.month] + 1) - fechaMenor.day; // SUMAMOS DIAS HASTA EL MES SIGUIENTE, INCLUYENDO EL DIA BISIESTO
            dias++; // PASAMOS DE MES                                 // SI LA FECHA MENOR FUESE 29 DE FEBRERO, SE HARIA UNA RESTA DE 28+1 - 29
                                                                      // YA SEA ENERO O FEBRERO, PASAMOS AL SIGUIENTE MES, SIN LA PREOCUPACION
                                                                      // DEL DIA BISIESTO DE AHI EN ADELANTE
            // APLICAMOS LOS CAMBIOS
            fechaMenor.month++;
            fechaMenor.day = 1;
        }else{ // SI NO ES BISIESTO EL AÑO O NO PASAMOS POR EL DIA BISIESTO, O LO QUE SEA, PASAMOS AL SIGUIENTE MES TRANQUILAMENTE
            dias += DIASMES[fechaMenor.month] - fechaMenor.day;
            dias++; // PASAMOS AL MES SIGUIENTE

            // APLICAMOS LOS CAMBIOS
            fechaMenor.month++;
            fechaMenor.day = 1;
        }

        // LO QUE ESTA CLARO ES QUE EL BISIESTO NO ES UNA PREOCUPACION, Y QUE LOS MESES PUEDEN SER IGUALES (O NO)
        // IGUALAMOS LOS MESES

        for(int m = fechaMenor.month; m < fechaMayor.month; m++){
            dias += DIASMES[m]; // PASAMOS DE MESES
            // APLICAMOS LOS CAMBIOS
            fechaMenor.month++;
        }
    }

    // SALIMOS DEL CASO 1 Y DEL CASO 2 CON LA UNICA NECESIDAD DE RETORNAR LA DIFERENCIA DE DIAS, PARA TERMINAR ESTA GRAN FUNCION
    dias += fechaMayor.day - fechaMenor.day;
    return dias;
    // APLICAMOS LOS CAMBIOS ( O NO :D )
 }

« Última modificación: 11 de Febrero 2016, 11:57 por César Krall »

César Krall

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 2078
  • No vales por lo que dices, sino por lo que haces
    • Ver Perfil
    • aprenderaprogramar.com
Hola! Lo veo un ejercicio muy completo y con bastantes comentarios que hacen más fácil que se pueda entender, le puede servir de ayuda a quienes estén aprendiendo programación c++

Buen aporte para los foros

Gracias!
Responsable de departamento de producción aprenderaprogramar.com

candidatopardo

  • Visitante
Hola de nuevo César! Siii... el uso de comentarios esta pensado para facilitar la comprensión de la función principal para los que están aprendiendo c++. La función que calcula la diferencia entre dos fechas.

Para los que visitantes de este tema, yo mismo comprendo la importancia de sentir que es uno mismo el que se decide a resolver un problema por su propia cuenta. Y no había caído en que, este mismo problema, se puede resolver de diferentes maneras.

¿Alguien se anima a resolverlo por otros caminos?
Por ejemplo, yo en la función "int Diferencia(fecha fecha1, fecha fecha2);" sumo días a la fecha menor (almacenando los días que voy sumando en una variable) hasta alcanzar la fecha mayor.

¿Alguien se anima a hacerlo restando días a la fecha mayor hasta alcanzar la fecha menor?

O quien sabe, ¿Alguien se anima a resolverlo o a explicar otros caminos para resolver el mismo problema?

Lo dejo en el aire. Estaré pendiente compañeros. Cuidarse.

Mastermind

  • Experto
  • *****
  • Mensajes: 536
    • Ver Perfil
He encontrado esta forma para hallar diferencia de días entre fechas que parece muy compacta ya que implica muy poco código (haciendo uso de la librería ctime http://en.cppreference.com/w/cpp/header/ctime)

Código: [Seleccionar]
#include <iostream>
#include <ctime>

int main()
{
    struct std::tm a = {0,0,0,24,5,104}; /* June 24, 2004 */
    struct std::tm b = {0,0,0,5,6,104}; /* July 5, 2004 */
    std::time_t x = std::mktime(&a);
    std::time_t y = std::mktime(&b);
    if ( x != (std::time_t)(-1) && y != (std::time_t)(-1) )
    {
        double difference = std::difftime(y, x) / (60 * 60 * 24);
        std::cout << std::ctime(&x);
        std::cout << std::ctime(&y);
        std::cout << "difference = " << difference << " days" << std::endl;
    }
    return 0;
}


Salida que se obtiene:

Thu Jun 24 01:00:00 2004
Mon Jul 05 01:00:00 2004
difference = 11 days

candidatopardo

  • Visitante
Mastermind, muchas gracias por mostrar un ejemplo de calculo de diferencia de días entre dos fechas utilizando la biblioteca estandar ctime. ¡Esta genial!

Me ha costado un poco entenderlo al principio pero gracias al enlace que has pasado y tras un ratito investigando ya esta mas que claro.

Cuídate.

 

Sobre la educación, sólo puedo decir que es el tema más importante en el que nosotros, como pueblo, debemos involucrarnos.

Abraham Lincoln (1808-1865) Presidente estadounidense.

aprenderaprogramar.com: Desde 2006 comprometidos con la didáctica y divulgación de la programación

Preguntas y respuestas

¿Cómo establecer o cambiar la imagen asociada (avatar) de usuario?
  1. Inicia sesión con tu nombre de usuario y contraseña.
  2. Pulsa en perfil --> perfil del foro
  3. Elige la imagen personalizada que quieras usar. Puedes escogerla de una galería de imágenes o subirla desde tu ordenador.
  4. En la parte final de la página pulsa el botón "cambiar perfil".