2012-04-25 13 views
6

Napisałem program, który powinien znaleźć dni między dwoma datami, ale ma pewne czkawki. Logika ma sens w mojej głowie, kiedy ją czytam, więc zakładam, że mam kilka błędów składniowych, które ciągle przeglądam, czy coś.C dni programu między dwiema datami

Po pierwsze, przy wprowadzaniu dwóch dat w różnych latach, moc wyjściowa jest zawsze wyłączona o około jeden miesiąc (w większości przypadków 31, ale 32 w jednym przypadku ... zobacz rysunek). Po drugie, dwie daty dokładnie w odstępie jednego miesiąca zwrócą liczbę dni w drugim miesiącu (tj. 1/1/1 do 2/1/1 daje 28). Są nieuniknione inne dziwne rzeczy, które ten program robi, ale mam nadzieję, że to wystarczająca informacja, aby pomóc wam zrozumieć, co robię źle. Dla mojego życia nie mogę sam tego wymyślić. Jestem stosunkowo nowy w C, więc proszę być łagodne =)

Dzięki

// Calculates the number of calendar days between any two dates in history (beginning with 1/1/1). 

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

void leap(int year1, int year2, int *leap1, int *leap2); 
void date(int *month1, int *day1, int *year1, int *month2, int *day2, int *year2, int *leap1, int *leap2); 

int main(void) 
{ 
     int month1, day1, year1, month2, day2, year2, leap1, leap2; 
     int daysPerMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31}; 
     int daysPerMonthLeap[] = {31,29,31,30,31,30,31,31,30,31,30,31}; 

     leap(year1, year2, &leap1, &leap2); 
     date(&month1, &day1, &year1, &month2, &day2, &year2, &leap1, &leap2); 

     if(year1 == year2) 
     { 
       int i, total; 

       if(month1 == month2)       // Total days if month1 == month2 
       { 
         total = day2 - day1; 
         printf("There are %d days between the two dates.", total); 
       } 
       else 
       { 
        if(leap1 == 1) 
         total = daysPerMonthLeap[month1] - day1; 
        else 
         total = daysPerMonth[month1] - day1; 

        for(i = month1 + 1; i < month2; i++)  // Days remaining between dates (excluding last month) 
        { 
         if(leap1 == 1) 
          total += daysPerMonthLeap[i]; 
         else 
          total += daysPerMonth[i]; 
        } 

        total += day2;        // Final sum of days between dates (including last month) 

        printf("There are %d days between the two dates.", total); 
       } 
     } 
     else             // If year1 != year2 ... 
     { 
       int i, total, century1 = ((year1/100) + 1) * 100, falseleap = 0; 

       if(leap1 == 1) 
        total = daysPerMonthLeap[month1] - day1; 
       else 
        total = daysPerMonth[month1] - day1; 

       for(i = month1 + 1; i <= 12; i++)    // Day remaining in first year 
       { 
        if(leap1 == 1) 
         total += daysPerMonthLeap[i]; 
        else 
         total += daysPerMonth[i]; 
       } 

       for(i = 1; i < month2; i++)      // Days remaining in final year (excluding last month) 
       { 
        if(leap2 == 1) 
         total += daysPerMonthLeap[i]; 
        else 
         total += daysPerMonth[i]; 
       } 

       int leapcount1 = year1/4;      // Leap years prior to and including first year 
       int leapcount2 = year2/4;      // Leap years prior to and NOT including final year 
       if(year2 % 4 == 0) 
         leapcount2 -= 1; 

       int leaptotal = leapcount2 - leapcount1;  // Leap years between dates 

       for(i = century1; i < year2; i += 100)   // "False" leap years (divisible by 100 but not 400) 
       { 
         if((i % 400) != 0) 
           falseleap += 1; 
       } 

       total += 365 * (year2 - year1 - 1) + day2 + leaptotal - falseleap;  // Final calculation 
       printf("There are %d days between the two dates.", total); 
     } 
     return 0; 
} 

void leap(int year1, int year2, int *leap1, int *leap2)    // Determines if first and final years are leap years 
{ 
     if(year1 % 4 == 0) 
     { 
       if(year1 % 100 == 0) 
       { 
         if(year1 % 400 == 0) 
           *leap1 = 1; 
         else 
           *leap1 = 0; 
       } 
       else 
         *leap1 = 1; 
     } 
     else 
       *leap1 = 0; 

     if(year2 % 4 == 0) 
     { 
       if(year2 % 100 == 0) 
       { 
         if(year2 % 400 == 0) 
           *leap2 = 1; 
         else 
           *leap2 = 0; 
           } 
       else 
         *leap2 = 1; 
     } 
     else 
       *leap2 = 0; 
} 

void date(int *month1, int *day1, int *year1, int *month2, int *day2, int *year2, int *leap1, int *leap2) 
{ 
     for(;;)      // Infinite loop (exited upon valid input) 
     { 
       int fail = 0; 
       printf("\nEnter first date: "); 
       scanf("%d/%d/%d", month1, day1, year1); 
       if(*month1 < 1 || *month1 > 12) 
       { 
         printf("Invalid entry for month.\n"); 
         fail += 1; 
       } 
       if(*day1 < 1 || *day1 > 31) 
       { 
         printf("Invalid entry for day.\n"); 
         fail += 1; 
       } 
       if(*year1 < 1) 
       { 
         printf("Invalid entry for year.\n"); 
         fail += 1; 
       } 
       if(daysPerMonth[month1] == 30 && *day1 > 30) 
       { 
         printf("Invalid month and day combination.\n"); 
         fail += 1; 
       } 
       if(*month1 == 2) 
       { 
         if(*leap1 == 1 && *day1 > 29) 
         { 
          printf("Invalid month and day combination.\n"); 
          fail += 1; 
         } 
         else if(*day1 > 28) 
         { 
          printf("Invalid month and day combination.\n"); 
          fail += 1; 
         } 
       } 
       if(fail > 0) 
         continue; 
       else 
         break; 
     } 

     for(;;) 
     { 
       int fail = 0; 
       printf("\nEnter second date: "); 
       scanf("%d/%d/%d", month2, day2, year2); 
       if(*year1 == *year2) 
       { 
         if(*month1 > *month2) 
         { 
           printf("Invalid entry.\n"); 
           fail += 1; 
         } 
         if(*month1 == *month2 && *day1 > *day2) 
         { 
           printf("Invalid entry.\n"); 
           fail += 1; 
         } 
       } 
       if(*month2 < 1 || *month2 > 12) 
       { 
         printf("Invalid entry for month.\n"); 
         fail += 1; 
       } 
       if(*day2 < 1 || *day2 > 31) 
       { 
         printf("Invalid entry for day.\n"); 
         fail += 1; 
       } 
       if(*year2 < 1) 
       { 
         printf("Invalid entry for year.\n"); 
         fail += 1; 
       } 
       if(daysPerMonth[month2] == 30 && *day2 > 30) 
       { 
         printf("Invalid month and day combination.\n"); 
         fail += 1; 
       } 
       if(*month2 == 2) 
       { 
         if(*leap2 == 1 && *day2 > 29) 
         { 
          printf("Invalid month and day combination.\n"); 
          fail += 1; 
         } 
         else if(*day2 > 28) 
         { 
          printf("Invalid month and day combination.\n"); 
          fail += 1; 
         } 
       } 
       if(fail > 0) 
         continue; 
       else 
         break; 
     } 
} 
+0

Czy nie ma do tego lib? – mkoryak

+0

@mkoryak, prawdopodobnie jest to dość przyzwoity problem z uczeniem się ... – sarnold

+0

Dla miesiąca 1 = 1, czy jest to styczeń lub luty w kodzie (na podstawie tablicy daysPerMonth)? Myślę, że jest to jeden z problemów w twoim kodzie. Kodujesz przedrostek element = 0 w obu tablicach lub redukujesz miesiąc o 1 podczas wykonywania obliczeń. – spicavigo

Odpowiedz

3

zredukować wszystkie indeksy miesiącu 1.

Co mogę powiedzieć, jest styczeń będzie odpowiadać daysPerMonth[0] lub daysPerMonthLeap[0] i nie daysPerMonth[1] lub daysPerMonthLeap[1]. Powodem tej będących indeksami tablicy zaczynają się od 0.

Więc gdziekolwiek używasz month1, month2 wewnątrz daysPerMonth[] lub daysPerMonthLeap[] użyć month1-1 i month2-1 zamiast.

Mam nadzieję, że jest to wystarczająco jasne. W przeciwnym razie prosimy o komentarz.

+0

Yup, haha. Naprawiono to i działa pięknie. = D – Andbrik

+0

@Andbrik, czy chciałbyś udostępnić swój ostatni kod? – bob90937

6

Po pierwsze, funkcja leap wydaje się zbyt skomplikowana; nie musisz wykonywać obu dat w jednym wywołaniu funkcji i jestem pewien, że można je napisać bardziej zwięźle, aby było to bardziej oczywiste. Oto wersja Mam ustanawiające wokół, że nie jest zwięzłe ale jestem przekonany, że jest łatwe do sprawdzenia logicznych:

int is_leap_year(int year) { 
     if (year % 400 == 0) { 
       return 1; 
     } else if (year % 100 == 0) { 
       return 0; 
     } else if (year % 4 == 0) { 
       return 1; 
     } else { 
       return 0; 
     } 
} 

można nazwać to tak:

int year1, year2, leap1, leap2; 
year1 = get_input(); 
year2 = get_input(); 
leap1 = is_leap_year(year1); 
leap2 = is_leap_year(year2); 

Brak wskaźników i znacznie mniej powielania kodu. Tak, wiem, że is_leap_year() można zredukować do pojedynczej instrukcji if(...), ale jest to dla mnie łatwe do przeczytania.

Po drugie, myślę, że ma niedopasowanie pomiędzy 0 indeksowanych tablic i 1-indeksowanych ludzkich miesięcy:

  if(*month1 < 1 || *month1 > 12) 

vs

int daysPerMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31}; 

trzecie, myślę, że dni w miesiącu może oblicza się nieco ładniej:

int days_in_month(int month, int year) { 
     int leap = is_leap_year(year); 
     /*    J F M A M J J A S O N D */ 
     int days[2][12] = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 
          {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; 
     if (month < 0 || month > 11 || year < 1753) 
       return -1; 

     return days[leap][month]; 
} 

Tutaj, zakładam, że styczeń to 0; będziesz musiał wymusić na reszcie kod. (Nauczyłem się tej sztuczki o podwójnym układzie od The Elements of Programming Style (page 54).) Najlepszą częścią używania takiej procedury jest to, że usuwa ona warunek skoku z obliczania różnicy.

czwarte, jesteś indeksowanie tablic poza ich granice:

  for(i = month1 + 1; i <= 12; i++) 
      { 
       if(leap1 == 1) 
        total += daysPerMonthLeap[i]; 

Jest to kolejny przykład problemu z 0 indeksowanych tablic i 1-indeksowanych miesięcy - ale mieć pewność, że to naprawić , także, gdy naprawiasz miesiące.

mam obawę, że nie znaleźli jeszcze wszystkich problemów - może się okazać, że łatwiej rodzaj pierwszym i drugim terminie po wejściu i usunąć cały ten kod weryfikacyjny - a następnie używać nazw before i after lub coś, co daje nazwy łatwiejsze do przemyślenia w skomplikowanym rdzeniu obliczeń.

+0

Przyjemny styl odniesienia - czy poleciłbyś pozbyć się oddzielnego bloku "if", gdzie lata są równe? Ogólne rozwiązanie powinno obsługiwać tę sprawę. – nvuono

+0

Dziękuję za pomoc. Okazuje się, że indeksowanie mojej tablicy było jedyną rzeczą, która mnie zepsuła * facepalm *. Działa teraz świetnie! – Andbrik

+0

@Andbrik: chociaż indeks tablicy był jedynym problemem, należy wziąć pod uwagę sugestie sarnolda, aby program był wydajny i krótki. – tumchaaditya

3

To nie jest kompletna odpowiedź. Chciałem tylko wspomnieć lepszy sposób obliczyć rok przestępny (ta pochodzi z The C Programming Language - Strona nr 41)

if ((year % 4 == 0 && year % 100 != 0) || year % 400 ==0) 
    printf("%d is a leap year \n", year); 
else 
    printf("%d is not a leap year \n", year); 
2

Zmień

int daysPerMonth[] = {31,28,31,30,31,30,31,31,30,31,30,31}; 
int daysPerMonthLeap[] = {31,29,31,30,31,30,31,31,30,31,30,31}; 

do

int daysPerMonth[] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; 
int daysPerMonthLeap[] = {0,31,29,31,30,31,30,31,31,30,31,30,31}; 

tj pad Macierze na początku, ponieważ cały kod opiera się na wartościach tablic, które zaczynają się od elementu 1, a nie od elementu 0.

Pozwoli to pozbyć się błędu, na który złożyłeś skargę.

Innym problemem jest błąd polegający na tym, że po dodaniu day2 do sumy. W obu przypadkach należy dodać day2 - 1 zamiast day2. Wynika to również z tego, że indeksy dat zaczynają się od 1 zamiast 0.

Po tym, jak wprowadziłem te zmiany (plus para, aby uzyskać kod do kompilacji), to działa poprawnie.

+0

o rzeczy day2 i day2-1: day2-1 da dni MIĘDZY tymi 2 datami (obie wykluczone). Ale, gdy zwykle obliczamy dni między 2 datami, zwykle przyjmujemy ten dzień. np. Różnica między 4 maja a 1 maja to 3 dni .. nie 2 dni ... – tumchaaditya

+0

To jest przypadek "day2 - day1', gdzie dni są w tym samym miesiącu. Te dwa przypisania do zmiany to te, w których dodawany jest dzień 2 bez odejmowania dnia 1. –

+0

Dziękuję, to dawało mi ból głowy lol. – Andbrik

1

W fragmencie kodu jest wiele problemów .. ale muszę przyznać, że jest to bardzo dobra próba. Istnieje wiele skrótów do tego, co próbujesz osiągnąć.

Napisałem następujący program, który znajduje liczbę dni między dwiema podanymi datami. Możesz użyć tego jako odniesienia.

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

char *month[13] = {"None", "Jan", "Feb", "Mar", 
        "Apr", "May", "June", "July", 
        "Aug", "Sept", "Oct", 
        "Nov", "Dec"}; 

/* 
daysPerMonth[0] = non leap year 
daysPerMonth[1] = leap year 
*/ 
int daysPerMonth[2][13] = {{-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 
          {-1, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; 

typedef struct _d { 
    int day;  /* 1 to 31 */ 
    int month;  /* 1 to 12 */ 
    int year;  /* any */ 
}dt; 

void print_dt(dt d) 
{ 
    printf("%d %s %d \n", d.day, month[d.month], d.year); 
    return; 
} 

int leap(int year) 
{ 
    return ((year % 4 == 0 && year % 100 != 0) || year % 400 ==0) ? 1 : 0; 
} 

int minus(dt d1, dt d2) 
{ 
    int d1_l = leap(d1.year), d2_l = leap(d2.year); 
    int y, m; 
    int total_days = 0; 

    for (y = d1.year; y >= d2.year ; y--) { 
     if (y == d1.year) { 
      for (m = d1.month ; m >= 1 ; m--) { 
       if (m == d1.month) total_days += d1.day; 
       else    total_days += daysPerMonth[leap(y)][m]; 
       // printf("%d - %5s - %d - %d \n", y, month[m], daysPerMonth[leap(y)][m], total_days); 
      } 
     } else if (y == d2.year) { 
      for (m = 12 ; m >= d2.month ; m--) { 
       if (m == d2.month) total_days += daysPerMonth[leap(y)][m] - d2.day; 
       else    total_days += daysPerMonth[leap(y)][m]; 
       // printf("%d - %5s - %d - %d \n", y, month[m], daysPerMonth[leap(y)][m], total_days); 
      } 
     } else { 
      for (m = 12 ; m >= 1 ; m--) { 
       total_days += daysPerMonth[leap(y)][m]; 
       // printf("%d - %5s - %d - %d \n", y, month[m], daysPerMonth[leap(y)][m], total_days); 
      } 
     } 

    } 

    return total_days; 
} 

int main(void) 
{ 
    /* 28 Oct 2018 */ 
    dt d2 = {28, 10, 2018}; 

    /* 30 June 2006 */ 
    dt d1 = {30, 6, 2006}; 

    int days; 

    int d1_pt = 0, d2_pt = 0; 

    if (d1.year > d2.year)  d1_pt += 100; 
    else      d2_pt += 100; 
    if (d1.month > d2.month) d1_pt += 10; 
    else      d2_pt += 10; 
    if (d1.day > d2.day)  d1_pt += 1; 
    else      d2_pt += 1; 

    days = (d1_pt > d2_pt) ? minus(d1, d2) : minus(d2, d1); 

    print_dt(d1); 
    print_dt(d2); 
    printf("number of days: %d \n", days); 

    return 0; 
} 

Wyjście jest w następujący sposób:

$ gcc dates.c 
$ ./a.out 
30 June 2006 
28 Oct 2018 
number of days: 4503 
$ 

Uwaga: nie jest to kompletny program. Brakuje sprawdzania danych wejściowych.

Mam nadzieję, że pomoże!

+0

Możesz użyć http://www.timeanddate.com/date/duration.html, aby sprawdzić swoje wyniki. –

0
//Difference/Duration between two dates 
//No need to calculate leap year offset or anything 
// Author: Vinay Kaple 
# include <iostream> 
using namespace std; 
int main(int argc, char const *argv[]) 
{ 
    int days_add, days_sub, c_date, c_month, b_date, b_month, c_year, b_year; 
    cout<<"Current Date(dd mm yyyy): "; 
    cin>>c_date>>c_month>>c_year; 
    cout<<"Birth Date(dd mm yyyy): "; 
    cin>>b_date>>b_month>>b_year; 
    int offset_month[12] = {0,31,59,90,120,151,181,212,243,273,304,334}; 
    days_add = c_date + offset_month[c_month-1]; 
    days_sub = b_date + offset_month[b_month-1]; 
    int total_days = (c_year-b_year)*365.2422 + days_add - days_sub+1; 
    cout<<"Total days: "<<total_days<<"\n"; 
    int total_seconds = total_days*24*60*60; 
    cout<<"Total seconds: "<<total_seconds<<"\n"; 
    return 0; 
} 
+0

Proszę podzielić się niektórymi informacjami na temat fragmentu kodu, zamiast tylko wklejać kod. – gmuraleekrishna

Powiązane problemy