Search code examples
clinuxcalendar

Suggestions for beginning steps to build a calendar program in C


I am working on a practice project of my school, which is to build a C program that resembles a calendar in linux or Mac command lines. The program doesn't have to implement all the methods that a calendar has, but it should produce the same output for these arguments

$ cal -m 11 2010
$ cal -m 13 2011
$ cal -m mar 2012
$ cal -m maRc 2013
$ cal -m MARCH 2014
$ cal -m MARCHY 2015
$ cal -m 5
$ cal 18
$ cal 2018
$ cal

How would I approach this? Is there any suggestion for beginning steps?

I would really appreciate any help.


Solution

  • This is a good challenge, and quite difficult if you try to do it in one fell swoop. You asked for suggestions, so I'll be happy to give you a few. The guiding principle here is to break the task of solving the full problem down into smaller, simpler steps.

    First, write some basic code to display a calendar at all. You probably want it to look something like this:

    Su Mo Tu We Th Fr Sa
     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
    

    For your first implementation, just blindly assume that the first day of the month is a Sunday. (We'll work to fix this and other assumptions later.) For your first implementation, either have the number of days in the month be hardwired, or have it be a command-line argument. Make sure the code works for months of length 28, 29, 30, or 31 days.

    This first subproblem obviously just involves some well-chosen nested loops and printf calls. But this will keep you busy for a while, especially if you're a beginner.

    Next, figure out how to make it work for months where the first day of the month isn't Sunday. You'll probably want to make it look something like this:

    Su Mo Tu We Th Fr Sa
              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
    

    Again, this will involve some loops and printf calls. Again (for now), don't worry about printing the calendar for an actual month — the sub-task here is, "print a calendar for a month of N days, starting on day-of-the-week M (for M in 0..6)." Again, you can have a hardwired value of M and just edit and recompile to change it, or pass it in on the command line. Make sure it works for all seven possibilities for M.

    Depending on how you wrote your first program (the one that assumed Sunday), you might be able to make a few incremental modifications, but if there's no clean way to add the day-of-the-week variability, don't be afraid to throw just about everything away and start over.

    Next, if you've been hardwiring the values of N and M and recompiling every time you change them, do take time to figure out how to read these values from the command line.

    Finally, we come to the hard part. (But don't worry too much if you thought the preceding work was plenty hard — this next step is hard in a different way.) Obviously to make a real calendar program, we're going to have to stop using generic N and M values, and start computing the actual length of an actual month, and the day of the week on which it actually starts.

    There's no function (in C and Unix, at least) that directly tells you how many days there are in a given month. There's no function that directly tells you the day of the week for the first of the month.

    It's straightforward to hardwire a 12-element array containing the number of days in each month, although you obviously have to do something special for February in leap years. (See here for tips on accurately computing which years are leap years.) But how to figure out which day of the week a month starts on? This is the same problem as "How can I find the day of the week given the date?", where the date is January 1, or February 1, or whatever month it is you're working on. I think there are two general approaches for solving this problem:

    1. Work it out from first principles. My go-to algorithm for this problem is an old chestnut called "Zeller's congruence", although there are a few alternative chestnuts as well. The old C FAQ list has several of them in question 20.31.

    2. Let the machine do the dirty work. To find the day of the week given the date, construct a struct tm containing the desired date in the tm_year, tm_mon, and tm_mday fields. (Remember that tm_mon is 0-based, and tm_year is 1900-based.) Fill in tm_hour, tm_min, and tm_sec with 12:00:00. Fill in tm_isdst with 0 or -1. Call mktime. On output, tm_wday will contain the day of the week for that date.

    Lastly, if you've gotten this far, you can worry about the command line input format. If it was me, I'd settle for just parsing

    cal 7 2018
    

    to accept the month and year number as integers. If you want to accept all the other variants you mentioned, that's going to be a certain amount of somewhat tedious, fussy work. There are lots of ways to do it; I'll let you discover some techniques that work for you.

    Finally, as an extra-credit challenge (and this is a real challenge!), as Bathsheba suggested in a comment, try making your program do the same thing as the standard Unix/Linux cal command does if you invoke it as

    cal 9 1752