Hi everyone!
I'm trying to learn C from my dad's old college textbooks, and I'm doing some of the problems he had to do. This problem is meant to calculate the number of days between two dates given by the user. I made a function to account for leap years, and to input the data. My program is running, but is coming up with numbers that are just a few days off. I already wrote this program in Pascal, and it works, but I'm trying to practice it in C.

Here's the source code.

/*This program counts the days between two dates input by the user*/
#include <stdio.h>

int yr1; /*global variables because all these need to be visible by multiple functions*/
int yr2; /*I don't know how to use pointers to return more than 1 variable from a function*/
int m1;
int m2;
int d1;
int d2;
int leap;

main()
{
      int numdays [13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /*13 members*/
      int totaldays; /*so that 1 would match up with January, fixing notation problems*/
      int year;
      int month;
      int day; 
      inputdata();
      for (year = (yr1 + 1); year < yr2; year ++) /*processes complete years*/
      {
          leapyr(year);
          if (leap == 1)
             totaldays += 366;
             else totaldays += 365;
      }
      year = yr1;
      for(month = m1 + 1; month <= 12; month++) /*processes incomplete year 1*/
      {
          leapyr (year);
          if (leap == 1)
             numdays[2] = 29;
             else numdays[2] = 28;
          totaldays = totaldays + numdays[month];
      }
      month = m1;
      for(day = d1; day <= numdays [month]; day++) /*processes incomplete month 1*/
      {   
          leapyr (year);
          if (leap == 1)
          numdays[2] = 29;
          totaldays += 1;
      }

      year = yr2;
      for(month = m2 - 1; month >= 1; month--) /*processes incomplete year 2*/
      {
          leapyr (year);
          if (leap == 1)
          numdays[2] = 29;
          totaldays += numdays[month];
      }
      month = m2;
      for(day = d2; day > 1; --day) /*processes incomplete month 2*/
      {
          totaldays += 1; /*no need for leapyr function here- don't need to know month max*/
      }
      printf("Total number of days between two given dates is: %d days", totaldays);
      getch();
          
          
}

int inputdata() /*function works as planned- already tested*/
{
     printf("This program calculates the number of days between two given dates\n");
     printf("Enter beginning date in form month/day/year: ");
     scanf( "%d/%d/%d", &m1, &d1, &yr1);
     printf("Enter ending date in form month/day/year: ");
     scanf("%d/%d/%d", &m2, &d2, &yr2);
}

int leapyr (year)
{
    int leap = 0;
    if (year % 4 == 0)
       leap = 1;
       else
       if (year % 100 == 0)
          leap = 0;
          else
          if (year % 400 == 0)
             leap = 1;
    printf("leap is: %d\n", leap);
    return leap;
}

Thanks for the help!

here's one problem that immediately jumps out:

leapyr (year);
if (leap == 1)

you need to assign the variable leap to the return value of the function

leap = leapyr(year)

but, more importantly, you should really consider using the standard C library <time.h>

pay attention to the time_t and tm structures. it will reduce your code to a few lines to accomplish the same thing.


.

IMO the easiest way is to convert both date strings into time_t integers, then just subtract the two integers. That will give you the difference in seconds. After that its just dividing up the seconds into minutes, hours, and days.

The instructions say nothing about calculating the number of years between two dates. so leap years are not relevant to the problem.

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

time_t tokenizedate(const char* datestr)
{
    struct tm tm;
    memset(&tm,0,sizeof(struct tm));
    tm.tm_mday = ((datestr[0] - '0') * 10) + (datestr[1] - '0');
    tm.tm_mon = ((datestr[2] - '0') * 10) + (datestr[3] - '0') - 1;
    tm.tm_year = atoi(&datestr[4]) - 1900;
    return mktime(&tm);
}

int main()
{
    time_t t1, t2;
    char date1[40], date2[40];
    time_t seconds = 0, minutes = 0, hours = 0, days = 0, months = 0;
    printf("Enter first date (DDMMYYYY format)\n");
    fgets(date1, sizeof(date1), stdin);
    printf("Enter second date (DDMMYYYY format)\n");
    fgets(date2, sizeof(date2), stdin);
    t1 = tokenizedate(date1);
    t2 = tokenizedate(date2);
    seconds = t2 - t1;
    minutes = seconds / 60;
    hours = seconds /(60 * 60);
    days = seconds / (60 * 60 * 24);


}
Comments
mo better

you need to assign the variable leap to the return value of the function
.

I may be showing my ignorance... I thought return leap; was assigning leap the the return value of the function. Or is that something different?

I've never dealt with the library functions in this program... I've only been programming in C for a couple days actually... So how can I figure out the functions in the library? Right now some of this is just gibberish to me.... Maybe it's just because I haven't had much experience yet though.

ahhh... i see what you're trying to do.... i wasnt really paying attention ... and no, what you're doing is not quite right.

think of this:

returnValue = myFunction(argument);

whatever the function *returns* is assigned via the assignment operator, in this case to the variable "returnValue".

the variable you pass in to the function, the "argument" can either be an input or an output or both.

in your case, leapyr(leap); you are passing the *value* of the argument, which in your example is "leap". leap, in your case is strictly undefined in the "main" routine, you have not given it a value. it may be zero, it may be negative eleventy-thousand, you dont know.

your function "leapyr" gets this undefined "leap" value and throws it around and changes it to something meaningful, then tries to *return* it back to the calling function (main), who ignores it, since the return value is unassigned.

meanwhile, "main" still has the original, locally-scoped variable "leap" which is still unknown, and anything that the "leapyr" function did to it was just temporary within the "leapyr" function, and so is lost on "leapyr"'s memory stack.

so, the easiest fix is to redefine your function, leapyr, to look like this:

int leapyr(void)
{
    int leap;   // locally defined, not the same variable in "main"

    // do your stuff

    return leap;
}

and when you call leapyr, call it like so

leap = leapyr();

it's very important that you understand this concept: the variable "leap" that you declared (but did not define) that is local to the "main()" function, will NOT be the same variable as "leap" defined local to the "leapyr" function. they will be two totally independent and completely unrelated variables, just like a guy named "Joe Smith" in New York is unrelated to some other "Joe Smith" in Los Angeles.

the only way they are connected, is that the numeric *VALUE* that the one contains is *RETURNED* through the function and that value is assigned to the other. the variables may as well have two different names, there would be no difference.

I understand that declaring leap in the main function and in the leapyr function are different, but that's why I declared leap globally, which I know is bad practice, but I did it anyway. I thought that made leap visible to all functions, and thus any changes applied by one function could be seen by other functions. Is this in concept wrong?

I've never dealt with the library functions in this program... I've only been programming in C for a couple days actually... So how can I figure out the functions in the library? Right now some of this is just gibberish to me.... Maybe it's just because I haven't had much experience yet though.

well, you're doing fine then. good job programming your day calculator "the hard way" :)

library functions are very powerful. essentially, they're just routines someone else has written to do some operation that is commonly used.

like "printf" is a library function (found in the <stdio.h> library) to print stuff to the terminal. <time.h> is another such library. learn the rules to use the functions in that library and life gets much easier.

look at Ancient Dragon's example above. it's probably a bit complex to you right now, but its easy to understand if you spend some time on it.

teh best way to learn is to use a debugger and "step through" the code one line at a time and watch the variables as they change values.

I understand that declaring leap in the main function and in the leapyr function are different, but that's why I declared leap globally

good lord, i really am not paying attention, am i?

i'm sorry.

let me re-look at this.

When I compiled this and added a few printf lines to help with the debugging before I asked for help, the leapyr function seemed to be working as planned. The problem was located somewhere else.

When I compiled this and added a few printf lines to help with the debugging before I asked for help, the leapyr function seemed to be working as planned. The problem was located somewhere else.

no actually the problem is there. and it's related to what i was talking about, 'local scope'

you have the variable 'leap' declared (but not defined) globally. there's no telling what this value may be

THEN you declare the variable 'leap' as an integer, local to the function "leapyr". this function calculates whether or not the year is a leap year (incorrectly, i might add... more on that later).

your print statement within "leapyr" prints out the value it calculates, and then attempts to return that value to the calling function (main)

however, "main" is not accepting the return value, as i mentioned previously, because you have no assignment. you probably think that the value "leap", defined globally, is modifed to whatever the "leapyr()" function changed it to.

But this is not the case.

because "leap" in the function "leapyr" is declared locally, it is modified local to the function's local runtime stack, only. the global variable "leap" is untouched. Like my analogy of the two "Joe Smiths" living 3000 miles away. here is the danger of using the same variable names, you confuse the programmer.

the quickest fix is to completely remove the "int leap;" declaration in your "leapyr" function, then it will use the global variable.

that said, globally-scoped variables, except in certain circumstances when properly used, are generally a very poor way to program. you see you have so much trouble with this trivial program, imagine 50,000 lines of code littered with globals!

you have other problems that may not be noticed on your compiler: you dont initialize variables such as "totaldays". some compilers automatically initialize to zero. many do not. this code will be broken in many environments.

you need to declare your functions prototypes before being called. your compiler doesnt care, but many (most!) will. functions that are typed with a return value, must return that type value. "int inputdata" must return an int, or better yet, be declared as "void inputdata"

finally your leap year calculation is wrong. the case "year % 4 == 0" will always return a leap = TRUE, and the other conditionals will therefore always be ignored. it should be

leap = 0;  //always define vars first!
if (year % 4 == 0)
    leap = 1;
if (year % 100 == 0)
    leap = 0;
if (year % 400 == 0)
    leap = 1;

or, preferably:

if (year % 400 == 0)
    leap = 1;
else if (year % 100 == 0)
    leap = 0;
else if (year % 4 == 0)
    leap = 1;
else 
    leap = 0; // or force default defines!!

.

man difftime
[edit]This has similarities to the original topic.

Slight nitpick:

IMO the easiest way is to convert both date strings into time_t integers, then just subtract the two integers.

A time_t is not necessarily an integer: time_t (and clock_t ) "are arithmetic types capable of representing times;" where "nteger and floating types are collectively called arithmetic types." The oddball method that comes to mind as a possible example is Excel's floating point date handling. It may be unlikely, but I believe that's why the standard library provides functions so we don't have to worry about these specifics.

Comments
mo better

So here is my new leapyr function

int leapyr (year)
{
     leap =0;
     if (year % 4 == 0)
     leap = 1;
     if (year % 100 == 0)
     leap = 0;
     if (year % 400 == 0)
     leap = 1;
     printf("leap is: %d\n", leap);
}

It seems to work correctly, but the program is still returning incorrect numbers. For example, it says that the number of days between 1/1/2001 and 1/1/2002 is 364.

looks good.

still, you should get in the habit of

(1) defining declared variables
(2) declaring prototypes of functions.
(3) returning values from functions according to prototype.

otherwise a bunch of minor short terms gains that you get away with on your current compiler will, eventually, come back and bite you in the ass for some long term pain :)


Also, I really suggest that you learn how to use standard libraries, like <time.h>. the code snippet that Dave linked, above, is a concise and easy to understand example.

#include <stdio.h>
#include <time.h>

int days_diff(int y, int m, int d)
{
    time+t now, then;
    struct tm date = {0};
    
    date.tm_year = y - 1900;
    date.tm_mon = m - 1;
    date.tm_mday = d;
    
    if  (time (&now) != (time_t) (-1) )
    {
        then = mktime ( &date );
        if (then != (time_t) (-1) )
        {
                 fputs(ctime(&now), stdout);
                 fputs(ctime(&then), stdout);
                 return difftime(now, then) / (24 * 60 * 60);
        }
    }
    return 0;
}

int main(void)
{
    printf("days = %d\n", days_diff(1970, 3, 8) );
    return 0;
}

/* my output
Mon Aug 22 11:51:06 2005
Sun Mar 08 00:00:00 1970
days = 12951*/

Unfortunately, I have no idea what this does. There are functions I have never seen before. Can you direct me to somewhere where I can learn these functions to avoid doing things the hard way? The book I have doesn't teach this.

>>difftime()

I see absolutely no value in that function because all it does is subtract the two parameter values and return the result as a double. When time_t is an (unsigned)integer then the double return value is just plain inconvenient because it would have to be typecast back to time_t. So the program might as well just subtract the two time_t objects and be done with it.

My function or Dave's function? I'm confused with both.

Here's a quick attempt at adding comments:

#include <stdio.h>
#include <time.h>

int days_diff(int y, int m, int d)
{
   time_t now, then;
   struct tm date = {0}; /** Zero out the structure for later mktime() use.*/
   /**
    * Populate the year, month, and day of the structure -- accounting for the
    * standard manner for ranges.
    */
   date.tm_year = y - 1900;
   date.tm_mon  = m - 1;
   date.tm_mday = d;
   /**
    * Call time() to get the current time from which to obtain a difference.
    */
   if ( time ( &now ) != (time_t)(-1) )
   {
      /**
       * Attempt to convert the structure (previously populated with the year,
       * month, and day) into a time_t for use with difftime().
       */
      then = mktime ( &date );
      if ( then != (time_t)(-1) )
      {
         fputs(ctime(&now),  stdout); /** Merely display time/date "a". */
         fputs(ctime(&then), stdout); /** Merely display time/date "b". */
         /**
          * Find the difference "a" - "b".
          * The result of difftime() is a difference in seconds (and its type is
          * double). Dividing by seconds in a day is done to get number of days.
          */
         return difftime(now, then) / (24 * 60 * 60);
      }
   }
   return 0;
}

int main(void)
{
   printf("days = %d\n", days_diff(1970, 3, 8));
   return 0;
}

>>difftime()

I see absolutely no value in that function because all it does is subtract the two parameter values and return the result as a double. When time_t is an (unsigned)integer then the double return value is just plain inconvenient because it would have to be typecast back to time_t. So the program might as well just subtract the two time_t objects and be done with it.

Isn't that somewhat analogous to how the Y2K issue came about -- that rather than doing it the "right" way, the "easy" way was chosen?

>>that rather than doing it the "right" way, the "easy" way was chosen?

You consider calling a function just to subtract two numbers is the "right way":icon_eek: What a waste of cpu time.

>>that rather than doing it the "right" way, the "easy" way was chosen?

You consider calling a function just to subtract two numbers is the "right way":icon_eek: What a waste of cpu time.

You seem to consider that a time_t is a measure of seconds. This is not necessarily true. Is writing potentially buggy code a better use of CPU time?

Time type

Type capable of representing times and support arithmetical operations.

This type is returned by the time function and is used as parameter by some other functions of the <ctime> header.

It is almost universally expected to be an integral value representing the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC. This is due to historical reasons, since it corresponds to a unix timestamp, but is widely implemented in C libraries across all platforms.

http://www.cplusplus.com/reference/clibrary/ctime/time_t/

Comments
"almost", much like "non-portable", is pretty much what I've been saying.
This article has been dead for over six months. Start a new discussion instead.