Perpetual Calendar

cooldean used Ask the Experts™
I need a program that displays a Gregorian calendar for any year,  After Christ (Anno Domini).

The program must do the following:

*Take input from the user for the year and month. If a number between 1 and 12 is entered for the month, print the calendar for that month only. If 0 is entered for the month, print the calendar for the entire year (all 12 months). If a number greater than 12 is entered for month, default it to 0.
*Print each month's name with the month's calendar.  Format the output so that the days of the week line up. Hint: When calling printf, putting an integer between % and d in the formatting string will cause printf to print in the width specified by the integer. For example, printf("%2d", x); will print x in a field width of two characters, right-flushed. For more information on printf formatting, refer to the text books.
*It MUST contain at least the following functions:
int leap_year(int yr)
Determines if the given year, yr, is a leap year. Returns 1 if it is; 0, otherwise.

int find_year_day(int year)
Determines the day of the week on which the given year, yr starts and returns it.

int days_in_month(int year, int m)
Determines the number of days in a particular month, m, of a particular year, yr, and returns it.

You do not have to name the functions and arguments/parameters as outlined above.  Highly use these suggested
Input functions:
A function that accepts the year from the user, and returns it.
A function that accepts the month from the user, and returns it.
Output functions:
A function that prints the calendar for a given month.
A function that prints the calendar for a give year.
You must provide a prototype of every function in your program in a header file that you include in your source file using the #include directive.
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Top Expert 2006
Hi cooldean

you need to do your own homework... we can give you only hints for it
this question has been asked before and was very well answered by expert salte
I am reproducing the post with the link ... you should be able to code it on your own after reading this

also unix and linux systems come with the program "cal", which is a perpetual calendar, you can look into their source for help
Comment from Salte
Date: 08/08/2003 12:09AM IST  Comment  

looks very much like homework to me.

I can nudge you in the general direction though.

It is actually very simple. First some info about the gregorian calendar.

The gregorian calendar have a cycle in 400 years. This means that the calendar for 2003 is EXACTLY identical to the calendar of 1603 including the weekdays for each date.

Within the 400 year period the calendar may appear to be somewhat irregular but there's a pattern there for those who look ;)

In general once you know the weekday for the 1st of the first month you want to display and you know how many days are in each of the months you want to display you can actually start generate the calendar immediately. Perhaps even add in week numbers if you want.

Also, note that different locale start the week on different days. For example US start the week on sundays while here in Norway we count monday as the first day of the week.

If you want to make your calendar being able to display correctly based on locale you can do that too easily.

Make a matrix of 6 * 7 elements. The rows are one week while the columns are the weekdays starting with the first day of the week (for example sunday or monday) and ending with the last day of the week (saturday or sunday).

Now you want to distribute 28-31 days within this matrix in a proper manner.

If you display each month in one set of rows and then the next month in another set of rows then you can decide to let each month have a varying number of weeks to display so that if for example february in a regular year had the 1st on the 1st day of the week then it would be exactly 4 lines with one line for each week. This is very compact way but it doesn't work well if you want to display three months side by side or if you want to print the month on a standard form. In this case you want a fixed number of weeks and you should set the form to have 6 weeks. It may sound like a lot but there are 4 weeks plus some days in a month with 31 days and if you're unlucky the 1st of the month may start on the last day of a week and then you get 1 day for the first week (actually last day of the week from previous month), 7 days for second week, third, fourth and fifth each that makes a total of 29 days and then finally the remaining days in the sixth week.

As already mentioned, february might occupy only 4 weeks (4 rows) in that format and so two rows are blank. Often one row will be blank and the question is then how should you align the days. Should you leave the first row blank or the last row blank or both?

If you want to "center" the calendar within the matrix you will use the following rule:

If two weeks are blank (only for february if feb 1st is the 1st day of the week) then set first and last row empty and fill in the calendar in row 2 through 5 (both inclusive).

Otherwise If one week is blank fill in from first row until no more days to fill in (last week will be blank).

If no weeks are blank, you fill all the rows.

If you instead want to align it on the top you start filling in at first row and then in the case of february you have two blank rows at the end.

I would say the "center" format looks best if you want to print it on paper ;)

Ok, now the algorithm is simple enough. If the first day of the month is weekday W where W is 0 for the first day of the week and 6 for the last day of the week then on the first row that you want to fill in you place 1 into M{r][W]:

Month[r][W] = 1;

Then you just move on setting M{r][W+1] = 2; etc and when you get to W = 7 you increase r and set W to 0 and continue until all the days of the month has been filled in.

Do it like this:

int r = 0; // r is row number
// d is number of days in current month.
if (d == 28 && W == 0) // we have the special case with two blank weeks.
    r = 1; // start at week 1.

memset(Month,0,sizeof(Month)); // clear the month matrix to start with.
for (int dnum = 1; dnum <= d; ++dnum) {
    Month[r][W++] = dnum++;
    if (W == 7) {
         W = 0;

Once you have this matrix filled out it will have the dnum == Month[row][weekday] if the dnum'th day in the current month is on weekday weekday and is to be placed in row "row" of the month.

Next you are ready to display the calendar for this month just walk through the matrix and if the value there is 0 it means you should print some blanks there and if the value is non-zero you print out the value (remember to use 2 places for the value so if the value is less than 10 you should print a space first).

Be aware that W isn't exactly the same value as you get from time_t struct or whatever other data you use to compute the weekday number. Those functions always have 0 == sunday and 6 == saturday. If you're in US this is fine but if you're somewhere else that is not fine, so here is how you do it:

step 1. Get the weekday number w from the function. Here 0 == sunday and 6 == saturday.

step 2. You want to change this value to find W where 0 == first day of week and 6 == last day of week.

If you're in Norway or Germany or some such then you want W == 0 for monday, i..e. when w == 1.

So: W = w - "w for 1st day of week".

If w == the first day of the week this result in 0 which is what we want.

There's one more snag, the subtraction above moves the value outside the range 0-6 and you want to move it back in so if W < 0 you add 7.

if ((W = w - "w for 1st day of week") < 0)
   W += 7;

is the complete formula. Here "w for 1st day of week" can be an integer or a constant value which is 0 for US and 1 for countries where monday is first day of week and is 6 if saturday is the first day of the week.

So, just get the 1st weekday of the month and then move on from there. You can use mktime() to compute the weekday for a given date. mktime() is defined in the C library.

An additional note about weeknumbers. If you want to add in weeknumbers all you have to do is to keep track of the week number for the first week of the month and then display that along with each week. When you move to next row (increase r and set W to 0 above) you also incerase the week number.

One problem with weeknumbers is that they too vary from locale to locale. In US week number 1 is the week that start with the 1st sunday of the year. Any days in january before the first sunday is considered week number 0. This is kinda fake since it really is the same as the last week of december but as far as I know, a week that starts in december and end in early january will suddenly change week number when you change the year in the US.

In Norway (to make an example) we don't do that. Instead if the week that start in december and end in january is week number 52 then it stays week number 52 also in january. Also, this week may even be week number 1 so that week number 1 of year X may actually start in december in the previous year!

The rule is this:

The first week which is such that the majority of days are in the year, this week is given the number 1. This rule translates to the rule that the first thursday of januray is always in week number 1. Thus if january 1st is a monday, tuesday, wednesday or thursday then that week is number 1. If january 1st is on a friday, saturday or sunday then it is week number 52 or 53 from the previous year and week number 1 start on the first monday of january.

How can you configure this in such a way that you can provide simple parameters and compute the correct week number?

It is actually easier than you think. First, we give a name to the weekday number of the first day of a week. Let's call it W0. This value is 0 in the us and it is 1 in Norway. It is the "w of the first day of the week" given above.

Next we need to identify the weekday which is such that the first such weekday is week number 1. We can call this value W1. In the US this value is also 0 since the first sunday is always week number 1 while in norway it would be 4 since it is the first thursday which identifies week number 1.

Now, given a month, day and year how can you find the week number?

First you need to compute the day number within the year (sometimes called yearday): Let's call it U. January 1st is 0, january 2nd is 1 etc january 31st is 30 and february 1st is 31 etc...

Now, given W0 and W1 above you are almost ready to find the week number. We need one more piece of information. When is first day of week number 1 within the year?

Well, let's see... Let's look at U == 0  (January 1st) and get the weekday of it. Let's say this is Wjan1.

Now, clearly if we allow ourselves some freedome and let negative values of U correspond to the last few days of the previous year and similarly high values of U (after december 31st) correspond to the first few days of the following year we then have:

U - W gives a sunday, so clearly -Wjan1 gives the yearday for a sunday at or before january 1st and
W1-Wjan1 gives a W1th day of some week.

If this value is positive then it is the first such day of the year and that W1 day belongs to week number 1. If the value is negative then it is in the previous year and we have to add 7 days to get week number one and.

if W1 >= Wjan1 then the value is positive and it correspond to some date D2 == W1 + 1 - Wjan1 which is the first W1 day of january and it is week number 1. If this value is U2 then that weekday is W1 and so U2-W1 is a sunday and U2-W1+W0 is the first day of week number 1, so:

U2-W1+W0 == W1-Wjan1-W1+W0 == W0-Wjan1

so if W1 >= Wjan1 then W0-Wjan1 gives the yearday for the first day of week number 1.
Clearly then the first day of week number N is given by:   U == W0-Wjan1+(N-1)*7.

And we get: U == W0-Wjan1 + 7N - 7 and we get (U+Wjan1-W0+7)/7

If W1 < Wjan1 then the value is negative and week number one start one week later, so we get:
U == W0-Wjan1+7N and we get (U+Wjan1-W0)/7

Let's see how this works for some cases.

Let's say that W1 == 4 (thursday) and W0 == 1 (monday). This is norwegian rules (almost).

Also, let's say that january 1st is on a monday so Wjan1 == 1.

In this case we get: W1 > Wjan1 and so the rule is (U+Wjan1-W0+7)/7 == (U+1-1+7)/7 = (U+7)/7

U==0 is january 1st (monday) in week number (0+7)/7 == 1
U==1 is january 2nd (tuesday) in week number (1+7)/7 == 1
U==6 is january 7th (sunday) in week number (6+7)/7 == 1
U==7 is january 8th (monday again) in week number (7+7)/7 == 2

ok, what if Wjan1 was a tuesday (2) then we get: U+Wjan1-W0+7)/7 = (U+2-1+7)/7 = (U+8)/7
U==0 is january 1st (tuesday) in week number (0+8)/7 == 1. This week started on monday december 31st but this is the week that contain the first thursday.
U==1 is january 2nd (wednesday) in week number (1+8)/7 == 1.
U==5 is january 6th  (sunday) in week number (5+8)/7 == 1
U== 6 is january 7th (monday again) in week number (6+8)/7 == 2.

We see that if Wjan1 is any weekday from W0 (monday) to W1 (thursday) (both inclusive) this works out. Let's verify with the last day of this range: Wjan1 == thursday (4 same as W1).
Then the week number is (U + Wjan1-W0+7)/7 == (U+4-1+7)/7 == (U+10)/7
U==0 is january 1st (thursday) in week number (0+10)/7 == 1. This week start on monday december 29th but since the first thursday is january 1st this is week number 1.
U==1 is january 2nd (friday) in week number (1+10)/7 == 1.
U==3 is january 4th (sunday) in week number (3+10)/7 == 1.
U==4 is january 5th (monday) in week number (4+10)/7 == 2.

If Wjan1 is greater than W1, or smaller than W0 in this case we use the other formula:
If Wjan1 is a friday (5) then we get week number N == (U+Wjan1-W0)/7 == (U+5-1)/7 == (U+4)/7
U == 0 january 1st (friday) in week number (0+4)/7 == 0. This is actually last week of previous year.
U == 2 january 3rd (sunday) in week number (2+4)/7 == 0.
U == 3 january 4th (monday) in week number (3+4)/7 == 1.
If Wjan1 is saturday (6) we get week number N == (U+Wjan1-W0)/7 == (U+6-1)/7 == (U+5)/7
U == 0 january 1st (saturday) in week number (0+5)/7 == 0.
U == 1 january 2nd (sunday) in week number (1+5)/7 == 0.
U == 2 january 3rd (monday) in week number (2+5)/7 == 1.

Finally, the last possibility is Wjan1 is sunday (0) we get the formula:
week number N == (U+Wjan1-W0+7)/7 == (U+6)/7
U == 0 january 1st (sunday) in week number 6/7 == 0.
U == 1 january 2nd (monday) in week number (1+6)/7 == 1.

We see that in this latter case we also had to add the 7. The general rule is fairly simple:

step 1. Compute V = Wjan1-W0.

step 2. If (W1 < Wjan1) add 7 to V.

step 3. If the sum is still negative add 7 to V and if V >= 14 subtract 7 from it.

The week number is then given by N = (U + V) / 7. Where U is the yearday number.

If the week number N becomes 0 we need to do special adjustment, similarly if the week value N becomes too large we also need to adjust.

For US rules we do no adjustment. the value N is the week number.

For europe we count 0 as the last week of previous year and for a high value of N (53 or 54) we do the following:

When computing U + V if the value becomes too high it actually indicates that the week is first week of following year. To find out which you can simply subtract the number of days in the current year and use the resulting value to find the week number of following year. If that computation gives a week number of 1 it is week number 1 if it gives 0 then the week really is in current year and we keep it even if it is high.

If the value is 0 it is last week of previous year, add the number of days in previous year to the value and then use that to compute the week number for previous year. The value is never too high, we keep it no matter what it is.

Or you can do a quickie since we know that W1 is thursday and W0 is monday so the rules becomes simply:

If january first is on a monday through thursday then january 1st is always in week number 1.
If january first is a friday then it belongs to previous year. It is same week number as december 31st of that year. Now if january first is a friday then january first of previous year is either a thursday or a wednesday (previous year was a leap year) and this lead to the conclusion that january first of previous year is week number 1 and so 52 weeks later you get week number 53 so january 1st this year is on week number 53. (a year is exactly 52 weeks + 1 or 2 days).
If january first is a sunday then the january first of the previous year was a saturday or friday if previous year was a leap year. In either case january first of previous year was last week of the year two years before and so january first of this year is week number 52.

The problem is if january first fall on a saturday. In this case it depends on whether previous year was a leap year or not. If it was a leap year then january 1st of previous year was a thursday and so is week number 1. Consequently january 1st of this year is then week number 53. If previous year was not a leap year then january 1st of that year was on a friday and so january 1st of this year is week number 52.

Personally I consider the more generic solution to be the best but you can do a quickie if you limit the options for this calculation to only the 1. Week start on monday, 2. Week number 1 is the first week containing a thursday

I recall doing such a calendar program in Pascal on punchcards.
I was among the last students still being introduced to punchcards in '81.


Thanks for the help sunnycoder.  I've been working on this for like more than two days and I didn't know how to begin.  I knew what the general premises were but I didn't know how to even start writing the code, being a beginner in this.  

I'm gonna try to work on the suggestion you gave.  I'm wondering, though, what is memset?  I haven't learned that yet, and is there basic substitute for it that I can use instead of memset?

I looked through the website and saw the Pascal version but I have to do it in C language.  Does anyone use C anymore anyways?  I can't believe I have to learn C.

Again, I'm gonna try to figure out your hints....but if there's anyone out there who can simplify it, which I doubt for a Perpetual Calendar, please keep posting.  Thanks alot.

Hi cooldean,

I'll be glad to fill in any blanks.  (Sunnycoder's been pretty thorough!)

memset() is a function to set all of the bytes of a memory region to the same value.  The value is almost always 0, but it doesn't have to be.  The calling sequence is:

void  *memset (void  *Address, int Value,  int Number);

Basically, you pass the starting location (Address), the value you want in each byte (Value), and the number of bytes to set (Number).  In Sunnycoder's example was the line:

memset(Month,0,sizeof(Month)); // clear the month matrix to start with.

This says that starting with the first byte of the variable *Month*, 0 should be stored, then continue to store 0 into consecutive bytes until sizeof(Month) bytes have been set.  sizeof() is a C function that returns the number of bytes in a basic type (like char, int, float, etc), array, union, or structure.

C is a lot more common that you might think.  A lot of drivers are written in C.  A lot of the Windows operating system is written in C.  One of the two main GUI environments for linux is written in C, etc.

Besides.  C is also a foundation language.  If you understand C, it's pretty easy to jump to C++, C#, and Java.  By learning C first, you will have learned the foundation that is common among all of these languages.


Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial