views:

165

answers:

5

I am writing a program that get month and year from the user, and then print out the calendar. But the calendar is only correct in January (every two years).

How do I make the other months correct? What am I doing wrong?

#include "stdafx.h"

void printMonth (int* startDay, int* days);

int _tmain(int argc, _TCHAR* argv[])
{

    int startDay, days, year, month, a;

    printf("enter year: ");
    scanf("%d", &year);
    printf("enter month: ");
    scanf("%d", &month);

    a = year - 1;
    startDay = ((a * 365) + (a / 4) - (a / 100) + (a / 400) + 1) % 7;

    switch (month)
    {
    case 1: days = 31;
     break;
    case 2: days = 28;
     break;
    case 3: days = 31;
     break;
    case 4: days = 30;
     break;
    case 5: days = 31;
     break;
    case 6: days = 30;
     break;
    case 7: days = 31;
     break;
    case 8: days = 31;
     break;
    case 9: days = 30;
     break;
    case 10: days = 31;
     break;
    case 11: days = 30;
     break;
    case 12: days = 31;
     break;
    }

    printMonth (&startDay, &days);

    printf("\nstart day: %d\n", startDay);
    printf("\na: %d\n", a);

    return 0;
}

void printMonth (int* startDay, int* days)
{
    int weekDay;

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

    for (weekDay = 0; weekDay < *startDay; weekDay++)
     printf("    ");

    for (int dayCount = 1; dayCount <= *days; dayCount++)
    {
     if (weekDay > 6)
     {
      printf("\n");
      weekDay = 1;
     }
     else
      weekDay++;
     printf("%3d ", dayCount);
    }

printf("\n--- --- --- --- --- --- ---\n");

return;
}
A: 

dont forget leap year

Fredou
Yeah, I will calculate leap year after finish this part.
Programme Newbie
+2  A: 

Your call to printMonth is explicitly passing January's start day, not that month's start day.

Shmoopty
O...How to pass other month's start day???
Programme Newbie
Your "startday = ..." line shows that you have a formula for calculating the year's (therefore January's) start day. To pass another month's start day, you would need to calculate what that day is. Can you figure out the formula for figuring out a month's starting day?
Shmoopty
If a year's starting day is Sunday, for example, what day of the week will February start on? When you have the answer, how did you calculate it?
Shmoopty
+1  A: 

The startDay is the day of the week of the first day of the year. You need to pass the day of the week of the first day of the month.

Consider changing your switch statement into an array. Calculate February based on the leap year, the others can be statically assigned. Then you can loop through the array summing lengths to figure out how many days into the year you need to start, and add that into the calculation of startDay before the modulo 7, and you can pass the number of days of the year by indexing into the array.

Keep in mind that C arrays are 0-based. You should also check the validity of the user input.

If you aren't familiar with it, there is a cal command on *NIX systems that may help you check correctness of your output, but be warned: there are some weird things that go on in the history of calendars.

retracile
Thanks, but how to get other month's week day?
Programme Newbie
Answer updated.
retracile
+1  A: 

You are passing the same value for startDay to printMonth() regardless of what month is entered. Consider how many days have passed from the start of the year to the start of the given month and from that you can figure out what day the given month starts on.

Note that you're also not considering the fact that February could have 29 days if it's a leap year. Whether or not it is a leap year is important because the length of February affects what day the rest of the months start on.

A year is a leap year if it is evenly divisible by 4 (2008, 2012 are leap years) and not evenly divisible by 100 (1900 is not a leap year) unless it is divisible by 400 (2000 is a leap year). Therefore:

leap_year = (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0))
Daniel Vandersluis
I will consider leap year later, thanks for remind.
Programme Newbie
You actually need to consider leap years first in order to figure out what day of the week a month starts from because all months beyond February will be affected based on February being 28 or 29 days.
Daniel Vandersluis
For example, if February starts on a Sunday and is 28 days, March will also start on a Sunday. However, if it is 29 days, March will start on a Monday. So knowing how long February is before determining the start date of any month is important.
Daniel Vandersluis
Oh...sorry, what is the formula to calculate the leap year?
Programme Newbie
I've updated my answer.
Daniel Vandersluis
It's actually sort-of embedded in the calculation of `startDay`, though there it is calculating the total number of leap days that have occurred.
retracile
+3  A: 

1) Initialize all your variables.

int startDay, days, year, month, a;
startDay = -1;
days = -1;

In this case "-1" or "0" express a value which still has no real content provided by the user. In other cases, you may choose to initialize to a "plausible" valid default value. This helps debugging. It's very ugly to inspect debug values which are whatever garbage happened to be there when you created your variable.

I don't remember if C lets you do this in one line, like C++. One way or the other, do initialize them.

2) Check and validate the user's input.

printf("enter year: ");
scanf("%d", &year);
// Check validity of year here. If year not valid, ask again, abort, or anything.

3) Validate the logic of your code with assertions (do not do this to validate the user's input, of course; see 2).

assert(month >= 1);
assert(month <= 12);
switch (month)
{ // ...

void printMonth (int startDay, int days)
{
    assert(startDay >= 1);
    assert(startDay <= 31);
    assert(days >= 0);
    // ...

Double-check that you are actually in debug mode and that failed assertions do something at all. For one execution, just write assert(0); (I don't know if you have booleans in C). That should fail.

4) Do different things in different functions. Asking the user a month and doing anything "complex" with it later is a code smell.

Daniel Daranas
Why startDay and days = -1?
Programme Newbie
@Programme Newbie: You could use 0 too, or -5, or -1000, if you want. In your original code, the value was whatever value happened to be. With my suggested modification, you will first assign a value that you know is invalid (i.e. it is not a valid user entry, unless of course your customers are the ancient Greeks). Later you assign the value that the user entered, so when debugging, you are sure where everything comes from. In your example little unexpected can happen, but in a wider example it's typically a very good idea to initialize your integers to a "default" value.
Daniel Daranas
Concrete example from the code as posted: If the user provides a 0 or a 13 as the month, `days` won't get initialized before it is used.
retracile
Sorry, I just a freshman in programming. I still no idea to get other month's start day and make other months correct.
Programme Newbie
@Programme Newbie: What _algorithm_ will you use? Do it first in pen and paper, run through it writing down the names and current values of the variables, and go through it three or four times, changing the values. After you get the algorithm right, write it into code - following the basic tips I and others gave you.
Daniel Daranas