Programas para calcular fechas

Iniciado por NicolasPileci, 12 Mayo 2017, 20:50 PM

0 Miembros y 1 Visitante están viendo este tema.

NicolasPileci

Estimados,

Serian tan amables de indicarme que error tengo en estos dos programas:

1 - "Calcular fecha siguiente" (Me devuelve la misma fecha siempre)

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    int dia,
        mes,
        año;
}   tFecha;

void calcularFechaSiguiente (tFecha fecha);

int main()
{
    system ("color 0a");
    tFecha fecha;
    printf("Ingrese dia: \n\n");
    scanf("%d",&fecha.dia);
    printf("\nIngrese mes: \n\n");
    scanf("%d",&fecha.mes);
    printf("\nIngrese año: \n\n");
    scanf("%d",&fecha.año);
    calcularFechaSiguiente (fecha);
    printf("\nFecha final: %d/%d/%d",fecha.dia,fecha.mes,fecha.año);
    return 0;
}

void calcularFechaSiguiente (tFecha fecha)
{
    if(fecha.mes == 4 || fecha.mes == 6 || fecha.mes == 9 || fecha.mes == 11)
    {
        if(fecha.dia == 30)
        {
            fecha.dia = 1;
            fecha.mes++;
        }
        else
            fecha.dia++;
    }
    if(fecha.mes == 2)
    {
        if ((fecha.año % 4 == 0) && ((fecha.año % 100 != 0) || (fecha.año % 400 == 0)))
        {
            if(fecha.dia == 29)
            {
                fecha.dia = 1;
                fecha.mes++;
            }
            else
                fecha.dia++;
        }
        if(fecha.dia == 28)
        {
            fecha.dia = 1;
            fecha.mes++;
        }
        else
            fecha.dia++;
    }
    if(fecha.mes == 1 || fecha.mes == 3 || fecha.mes == 5 || fecha.mes == 7 || fecha.mes == 8 || fecha.mes == 10)
    {
        if(fecha.dia == 31)
        {
            fecha.dia = 1;
            fecha.mes++;
        }
        else
            fecha.dia++;
    }
    if(fecha.dia == 31)
    {
        fecha.dia = 1;
        fecha.mes = 1;
        fecha.año++;
    }
    else
        fecha.dia++;
}


1 - "Calcular dias entres dos fechas" (Me devuelve un resultado cualquiera)

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    int dia,
        mes,
        año;
}   tFecha;

long calcularFecha (tFecha fecha1, tFecha fecha2);

int main()
{
    system ("color 0a");
    tFecha fecha1,
           fecha2;
    long num;
    printf("Ingrese dia - Primera fecha: \n\n");
    scanf("%d",&fecha1.dia);
    printf("\nIngrese mes - Primera fecha: \n\n");
    scanf("%d",&fecha1.mes);
    printf("\nIngrese año - Primera fecha: \n\n");
    scanf("%d",&fecha1.año);
    printf("\nIngrese dia - Segunda fecha: \n\n");
    scanf("%d",&fecha2.dia);
    printf("\nIngrese mes - Segunda fecha: \n\n");
    scanf("%d",&fecha2.mes);
    printf("\nIngrese año - Segunda fecha: \n\n");
    scanf("%d",&fecha2.año);
    num = calcularFecha (fecha1, fecha2);
    printf("\nDias entre las dos fechas: %d.\n",num);
    return 0;
}

long calcularFecha (tFecha fecha1, tFecha fecha2)
{
long num = 0,
        i;
if(fecha1.año < fecha2.año)
        for(i=fecha1.año;i<fecha2.año;i++)
        {
            if ((i % 4 == 0) && ((i % 100 != 0) || (i % 400 == 0)))
                num += i*366;
            else
                num += i*365;
        }
    else
        for(i=fecha2.año;i<fecha1.año;i++)
        {
            if ((i % 4 == 0) && ((i % 100 != 0) || (i % 400 == 0)))
                num += i*366;
            else
                num += i*365;
        }
    if(fecha1.mes < fecha2.mes)
        for(i=fecha1.mes;i<fecha2.mes;i++)
        {
            if(i == 4 || i == 6 || i == 9 || i == 11)
                num += i*30;
            if(i == 1 || i == 3 || i == 5 || i == 7 || i == 8 || i == 10 || i == 12)
                num += i*31;
            if(i == 2)
                num += i*28;
        }
    else
        for(i=fecha2.mes;i<fecha1.mes;i++)
        {
            if(i == 4 || i == 6 || i == 9 || i == 11)
                num += i*30;
            if(i == 1 || i == 3 || i == 5 || i == 7 || i == 8 || i == 10 || i == 12)
                num += i*31;
            if(i == 2)
                num += i*28;
        }
if(fecha1.dia > fecha2.dia)
        num += fecha1.dia - fecha2.dia;
else
num += fecha2.dia - fecha1.dia;
    return num;
}


Desde ya muchas gracias.
Saludos!

MAFUS

#1
En el primero programa pasas la estructura por valor. Todos los cambios que hagas dentro de la función no se reflejarán fuera de ella, por eso debes pasarla por referencia (un puntero de ella o su dirección) y cuidado con el cambio de acceso a los datos del puntero a la estructura.

Para el segundo programa observa lo siguiente:
if(fecha1.año < fecha2.año)
        for(i=fecha1.año;i<fecha2.año;i++)
        {
            if ((i % 4 == 0) && ((i % 100 != 0) || (i % 400 == 0)))
                num += i*366;
            else
                num += i*365;
        }


Fíjate que num va a tener el número erróneo de días por la simple razón de:
Supongamos que el año es el 2000. Pues en la primera iteración del bucle será 2000*365, a este número, en la segunda iteración será 2000 * 365 + 2001 * 365 y así. La fórmula está mal.

NicolasPileci

Estimado,

Gracias por tu respuesta. Surgio otro problema dentro del primer programa y es que cuando ingreso un dia 30 y un mes 4, el resultado obtenido es 2/05/17. No puedo hallar el error.

Gracias!

MAFUS

Error de lógica. Tal como lo tienes tu programa hace lo siguiente:
if(fecha.mes == 4 || fecha.mes == 6 || fecha.mes == 9 || fecha.mes == 11)
{
    if(fecha.dia == 30)
    {
        fecha.dia = 1;
        fecha.mes++;
    }
    else
        fecha.dia++;
}

// ...

if(fecha.dia == 31)
{
    fecha.dia = 1;
    fecha.mes = 1;
    fecha.año++;
}
else
    fecha.dia++;


Cómo puedes ver ese último if de la función se ejecuta y como no es día 31 te sumará un día más.

NicolasPileci

Perdon,

Ya corregi el codigo, pero aun me sigue sumando un dia de mas en los meses de 30 dias.

void calcularFechaSiguiente (tFecha *fecha)
{
    if(fecha->mes == 4 || fecha->mes == 6 || fecha->mes == 9 || fecha->mes == 11)
    {
        if(fecha->dia == 30)
        {
            fecha->dia = 1;
            fecha->mes++;
        }
        else
            fecha->dia++;
    }
    if(fecha->mes == 2)
    {
        if (fecha->año % 4 == 0 && fecha->año % 100 != 0 || fecha->año % 400 == 0)
        {
            if(fecha->dia == 29)
            {
                fecha->dia = 1;
                fecha->mes++;
            }
            else
                fecha->dia++;
        }
        if(fecha->dia == 28)
        {
            fecha->dia = 1;
            fecha->mes++;
        }
        else
            fecha->dia++;
    }
    if(fecha->mes == 1 || fecha->mes == 3 || fecha->mes == 5 || fecha->mes == 7 || fecha->mes == 8 || fecha->mes == 10)
    {
        if(fecha->dia == 31)
        {
            fecha->dia = 1;
            fecha->mes++;
        }
        else
            fecha->dia++;
    }
    if(fecha->mes == 12)
    {
        if(fecha->dia == 31)
        {
            fecha->dia = 1;
            fecha->mes = 1;
            fecha->año++;
        }
        else
            fecha->dia++;
    }
}

MAFUS

De nuevo un error de lógica y parecido al de antes.

Los meses de 30 días están al principio de la función. Cuando los operas pasan a ser meses de 31 días PERO la función no se detiene y llega al código que opera los meses de 31 días. Tu variable ahora es un mes de 31 días y por tanto se le aplica el código de nuevo haciendo que se sume un día.

Tienes 3 formas de solucionar esto:
1. Pones return después de cada operación
2. Haces uso de else if
3. Usas un switch

Serapis

#6
El código es susceptible a errores, porque aunque o tratas bien, lo complicas... cometes el error de querer tratarlo en 'formato humano'....
Abstráete todo lo que puedas del asunto humano, y hazlo desde una perspectiva más matemática...entonces la solución queda sencilla (y el código más breve y legible)

Veamos... los meses son 12, el problema aparecerá pués en los días... que cada mes tiene su tela, icluído los años bisiestos...


//Entonces creamos un string que represente los meses. Y su contenido sean 12 caracteres
MESES= "@ABCDEFGHIJK" // empiezo en la arroba, si no es tu gusto, cambia como prefieras. En realidad, no lo usamos, se pone solo para que se vea de donde proceden los valores de las dos siguientes variables (sino quedaría oscuro).

//Ahora creamos otras también como string constantes
MesesDe31Dias = "@BDFGIK"  //esto es: enero, marzo, mayo, julio, agosto, octubre, diciembre
MeseeDe30Dias ="CEHJ" // esto es: abril, junio, septiembre, noviembre,
//MesesMenos30 = "A" //esto es, febrero... no se utiliza, se pone por claridad  


Ahora creamos la función: Básicamente simplifica la verificación de los meses y la actualización delos díasy mes, se invoca a otra función.

Funcion FechaSiguiente(Mes, Dia) boolean  //devolvemos false, si los datos de entrada no están en el rango...
  string M

  Si mes esta en el rango 1-12 luego
      M = (Mes + 63) //porque @ es el ASCII 64, y +63 porque mes está en el rango 1-12, no en rango 0-11
      Si MesesDe30Dias.Contiene(M) luego
          Si Dia esta en el rango 1-30 luego
               llamada FijarMesYDia(Dia, 30, Mes)
               Return TRUE
          Fin si
      Si no
          Si MesesDe31Dias.Contiene(M) luego
              Si Dia esta en el rango 1-31 luego
                  llamada FijarMesYDia(Dia, 31, Mes)
                  Return TRUE
              Fin si    
          Si no // el mes es febrero: porque está en rango y no es ninguno de los anteriores...
              Si Dia es menor de 28 luego
                      Incrementar Dia
              Si no //es día 28 ó 29, falta saber si es bisiesto
                   Si Año no es bisiesto // not ((año mod 4 = 0)   and (año mod 100 <> 0)) //falta la comprobación múltiplo de 400...
                       llamada FijarMesYDia(Dia, 28, Mes)
                   Si no // el año es bisiesto, es día 29?
                       llamada FijarMesYDia(Dia, 29, Mes)                                          
                   Fin si
                   Return TRUE
              Fin si    
          Fin si  
      Fin si
  Fin si
Fin funcion

Funcion FijarMesYDia( Dia, DiaLimite, Mes)
   si Dia < Dialimite luego
        Incrementar Dia
   Si no
       Si Mes < 12 luego   // MesLimite
           Incrementar Mes
       Si no
           Mes = 1
       Fin si
       Dia =1
   Fin si
Fin Funcion


Si necesitas velocidad (no parece quesea el caso, esto no se va allamar miles ni millones devecescadavez, supongo), puedes a la entrada cortar con una comprobación en la que la mayor parte delas veces va a caer, y solo unas pocas veces haría el restode comprobaciones:

Funcion FechaSiguienteRapido(Mes, Dia) boolean
    Si dia <28 luego
        Incrementar Dia
        Return TRUE
   Si no
       // el resto de veces, deberá comprobar los casos según meses, tal como se hace más arriba, pero solo cuando sean días 28,29,30 y 31
   Fin si
Fin Funcion


NicolasPileci

Muchas gracias a todos!

Un saludo.

engel lex

yo propondría una forma mucho más resumida... sería convertir en dias, calcular y convertir de vuelta

para calcular entre 2 dias
#include <stdio.h>
#include <stdlib.h>

const int ajuste[12] = {0,1,-1,0,0,1,1,1,2,2,3,3};

typedef struct
{
    int dia,
        mes,
        ano,
        dias_conv;
}   tFecha;

int main(){
  tFecha fecha1, fecha2;
  printf("Ingrese dia - Primera fecha: \n\n");
  scanf("%d",&fecha1.dia);
  printf("\nIngrese mes - Primera fecha: \n\n");
  scanf("%d",&fecha1.mes);
  printf("\nIngrese año - Primera fecha: \n\n");
  scanf("%d",&fecha1.ano);
  printf("\nIngrese dia - Segunda fecha: \n\n");
  scanf("%d",&fecha2.dia);
  printf("\nIngrese mes - Segunda fecha: \n\n");
  scanf("%d",&fecha2.mes);
  printf("\nIngrese año - Segunda fecha: \n\n");
  scanf("%d",&fecha2.ano);
 
  fecha1.mes -= 1;
  fecha1.dias_conv = fecha1.ano*365.24 + 30*fecha1.mes + ajuste[fecha1.mes] + fecha1.dia;
  fecha1.mes += 1;
 
  fecha2.mes -= 1;
  fecha2.dias_conv = fecha2.ano*365.24 + 30*fecha2.mes + ajuste[fecha2.mes] + fecha2.dia;
  fecha2.mes += 1;
 
  printf("\ndias entre fechas: %d", fecha2.dias_conv -fecha1.dias_conv );
  return 0;
}


da la fecha con erro de 1 dia pero me da pereza depurar
El problema con la sociedad actualmente radica en que todos creen que tienen el derecho de tener una opinión, y que esa opinión sea validada por todos, cuando lo correcto es que todos tengan derecho a una opinión, siempre y cuando esa opinión pueda ser ignorada, cuestionada, e incluso ser sujeta a burla, particularmente cuando no tiene sentido alguno.

MAFUS

Ala pues. Aquí va el de la fecha del día siguiente:
void obtener_siguiente_fecha(tFecha *fecha) {
    switch(fecha->mes) {
        case 1:
        case 3:
        case 5:
        case 7:
        case 8:
        case 10:
        case 12:
            if(fecha->dia == 31)
                fecha->dia = 0;
            break;
       
        case 4:
        case 6:
        case 9:
        case 11:
            if(fecha->dia == 30)
                fecha->dia = 0;
            break;
           
        case 2:
            if(fecha->dia == 28 + (!(fecha->anyo%4) && fecha->anyo%100) || !(fecha->anyo%400))
                fecha->dia = 0;
            break;
    }
    if(fecha->dia == 0)
        ++fecha->mes;
    if(fecha->mes == 13) {
        fecha->mes = 1;
        ++fecha->anyo;
    }
   
    ++fecha->dia;
}


Evitando que se repita código ;D