The Month Program, Phase III

With an algorithm that properly returns the first day of the month, the next step in creating a calendar is to display that first week, and then all subsequent weeks. Sounds easy, right?

Back when I first learned to program, I wouldn’t even bother trying to work out the details of which day of the week was the first of the month. No, I’d probably write a simple program that contained this line of code:

printf("Sun Mon Tue Wed Thu Fri Sat\n");

There! The calendar program is almost finished!

Of course, the next line of code would stump me.

I could always “cheat.” In fact, I’ve done exactly that: Once I programmed a web page calendar for a political campaign. Rather than write all the algorithms, I simply hard-coded each month. It was an easy solution that worked over the campaign’s few months, but not after that. (Re-read my earlier Lesson on how you don’t always have to code every solution.)

When displaying a monthly calendar, the first week is going to be special. You need to discover which weekday is the first and leave any earlier days of the week blank. After that week, populating subsequent weeks is fairly easy; a simple loop does the job.

In fact, it helps to think of a calendar as merely a way to format a series of numbers into seven columns. That’s all a calendar program is doing.

Last week’s Lesson contained an algorithm that returned the day of the week for the first day of the month. Here’s that function, where wday is the current day of the week (0 to 6 for Sunday to Saturday) and mday is the current day of the month (1 to 31):

/* Calculate First Day of the Month */
    first = wday - (mday % 7) + 1;
    first %= 7;
    if( first < 0 )
        first+=7;

The value generated by this algorithm (first) is in the range of 0 through 6 (Sunday to Saturday), which is consistent with the C library's time and date functions. Once you know that starting day, you can build the first week in a monthly calendar.

Just as the first week is important, so is the last. A monthly calendar is going to end on some day of the week. That day is easier to figure out because all but one month has a discrete number of days. February is the wild card, but I'll save that ugly month for next week's Lesson.

Here's code I concocted to display a monthly calendar. The days in the month are set at 31, which works for this month (January). Again, I'll address the issue of days in a month next week.

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

/* Set `days in month` as a constant for now */
#define DAYS_IN_MONTH 31

int thefirst(int weekday, int monthday);

int main()
{
    time_t tictoc;
    struct tm *rightnow;
    int wday,mday,month,year,firstdom,day,d;
    char *months[12] = {
        "January", "February", "March", "April",
        "May", "June", "July", "August",
        "September", "October", "November", "December" };

    time(&tictoc);                  /* get Unix epoch time */
                                    /* fill tm struct rightnow */
    rightnow = localtime(&tictoc);
                                    /* Get info from struct */
    wday = rightnow->tm_wday;       /* 0=Sunday */
    wday = rightnow->tm_wday;       /* 0=Sunday */
    mday = rightnow->tm_mday;       /* 1 to 31 */
    month = rightnow->tm_mon;       /* 0=January */
    year = rightnow->tm_year+1900;  /* current year - 1900 */

/* Display Calendar Header */
    printf("%s %d\n",months[month],year);
    printf("Sun Mon Tue Wed Thu Fri Sat\n");

/* Prep starting variables */
    firstdom = thefirst(wday,mday);
    day = 1;                        /* Day of the month */
/* Display the days of the month */
    while( day < DAYS_IN_MONTH )
    {
        /* There are seven days in a week
           These values correspond to 0 through 6
           which is what the `tm` sructure uses */
        for( d = 0; d < 7; d++)
        {
            /* Test for the first week of the month */
            if( d < firstdom && day == 1 )
            {
                printf("    ");     /* blank date, 4 spaces */
            }
            else
            {
                                    /* Display the day */
                printf(" %2d ",day);
                day++;              /* Tomorrow's date */
                                    /* Check the last dom */
                if( day > DAYS_IN_MONTH)
                    break;          /* Stop display */
            }
        }
        putchar('\n');              /* End of the week/month */
    }

    return(0);
}

/* Calculate First Day of the Month. On which day of the week
   does the first fall? Return as 0 to 6 for Sunday
   to Saturday */
int thefirst(int weekday, int monthday)
{
    int first;

    first = weekday - (monthday % 7) + 1;
    first %= 7;
    if( first < 0 )
        first+=7;

    return(first);
}

Don't let the code's length intimidate you. It could have been far shorter, but I spaced things out and added in a bunch of comments.

The meat of the program starts at Line 34. Variable day is initialized to 1, the first day of the month. The while loop from Lines 36 through 59 formats each week in the month. The value of the last day is set by the constant DAYS_IN_MONTH. So, as I wrote earlier, this routine merely formats a series of numbers, in this case 1 through 31.

Months are divided into weeks, so the while loop merely nests a for loop between Lines 41 and 57. Line 58 displays the newline at the end of each week.

In the for loop (Line 41), each week is ticked off 0 through 6 for Sunday through Saturday. An if test at Line 44 determines whether the first day of the month has been encountered. The firstdom variable holds the day of the week value (0 through 6) for the first day of the month, held in the day variable. As long as that condition hasn't been met, the printf() function at Line 46 displays a blank day of the week (four spaces).

Once the month starts, the else condition is executed. The day is displayed by a printf() statement at Line 51. Variable day is incremented to the next day of the month.

The if test at Line 54 determines whether the last day of the month has been displayed. If so, the for loop is broken (break at Line 55). At that point, with variable day greater than the days in the month, the while loop stops. The monthly calendar has been output.

Here's sample output for January 2014:

January 2014
Sun Mon Tue Wed Thu Fri Sat
              1   2   3   4
  5   6   7   8   9  10  11
 12  13  14  15  16  17  18
 19  20  21  22  23  24  25
 26  27  28  29  30  31

The key to understanding how the month is displayed is observing the two loops, while and for. The while loop handles the days of the month, the for loop handles days of the week.

Next Lesson covers calculating how many days are in February, which is a key part of creating any legitimate calendar program.

Leave a Reply