Jump to: navigation, search

The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 5.08 on page 112

There is no error-checking in day_of_year or month_day. Remedy this defect.



Solution by Lars Wirzenius

/*
 * A solution to exercise 5-8 in K&R2, page 112:
 *
 *	There is no error checking in day_of_year or month_day. Remedy
 *	this defect.
 *
 * The error to check for is invalid argument values. That is simple, what's
 * hard is deciding what to do in case of error. In the real world, I would
 * use the assert macro from assert.h, but in this solution I take the
 * approach of returning -1 instead. This is more work for the caller, of
 * course.
 *
 * I have selected the year 1752 as the lowest allowed year, because that
 * is when Great Britain switched to the Gregorian calendar, and the leap
 * year validation is valid only for the Gregorian calendar.
 *
 * Lars Wirzenius <liw@iki.fi>
 */

#include <stdio.h>

static char daytab[2][13] =  {
	{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
	{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
};

/* day_of_year: set day of year from month & day */
int day_of_year(int year, int month, int day)
{
	int i, leap;
	
	if (year < 1752 || month < 1 || month > 12 || day < 1)
		return -1;

	leap = (year%4 == 0 && year%100 != 0) || year%400 == 0;
	if (day > daytab[leap][month])
		return -1;

	for (i = 1; i < month; i++)
		day += daytab[leap][i];
	return day;
}

/* month_day: set month, day from day of year */
int month_day(int year, int yearday, int *pmonth, int *pday)
{
	int i, leap;
	
	if (year < 1752 || yearday < 1)
		return -1;

	leap = (year%4 == 0 && year%100 != 0) || year%400 == 0;
	if ((leap && yearday > 366) || (!leap && yearday > 365))
		return -1;

	for (i = 1; yearday > daytab[leap][i]; i++)
		yearday -= daytab[leap][i];
	*pmonth = i;
	*pday = yearday;
	
	return 0;
}


/* main: test day_of_year and month_day */
int main(void)
{
	int year, month, day, yearday;
	
	for (year = 1970; year <= 2000; ++year) {
		for (yearday = 1; yearday < 366; ++yearday) {
			if (month_day(year, yearday, &month, &day) == -1) {
				printf("month_day failed: %d %d\n",
					year, yearday);
			} else if (day_of_year(year, month, day) != yearday) {
				printf("bad result: %d %d\n", year, yearday);
				printf("month = %d, day = %d\n", month, day);
			}
		}
	}
	
	return 0;
}


Solution by menonsahab's

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

static char daytab[2][13] = 
{
	{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
	{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

int day_of_year(int year, int month, int day)
{
	if( year < 1752 )
		return -1; 

	int i, leap;
	leap = (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);

	if( !((month >= 1 && month <= 12) && (day >= 1 && day <= daytab[leap][month])) )
		return -1;

	for(i = 1; i < month; i++)
	{
		day += daytab[leap][i];
	}
	return day;
}

void month_day(int year, int yearday, int *pmonth, int *pday)
{
	if( year < 1752 )
	{
		*pmonth = *pday = -1;
		return;
	}

	int i, leap;
	leap = (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);

	if( !(yearday >= 1 && yearday <= (leap ? 366 : 365)))
	{
		*pmonth = *pday = -1;
		return;
	}

	for(i = 1; yearday > daytab[leap][i]; i++)
		yearday -= daytab[leap][i];
	*pmonth = i;
	*pday = yearday;
}

int main()
{
	int month, day, year, yearday, retval;

	/******************************* Case 1 *****************************/
	printf("Case 1:\n");
	year = -2017;
	month = 11;
	day = 22;
	yearday = 326;

	retval = day_of_year(year, month, day);
	if(retval == -1)
		printf("Incorrect input\n");
	else
		printf("%d %d %d <---> %d %d\n", year, month, day, year, retval);

	month_day(year, yearday, &month, &day);
	if(month == -1 && day == -1)
		printf("Incorrect input\n");
	else
		printf("%d %d <---> %d %d %d\n", year, yearday, year, month, day);
	printf("\n");
	/*********************************************************************/

	/******************************* Case 2 *****************************/
	printf("Case 2:\n");
	year = 2017;
	month = -11;
	day = 22;
	yearday = 326;

	retval = day_of_year(year, month, day);
	if(retval == -1)
		printf("Incorrect input\n");
	else
		printf("%d %d %d <---> %d %d\n", year, month, day, year, retval);

	month_day(year, yearday, &month, &day);
	if(month == -1 && day == -1)
		printf("Incorrect input\n");
	else
		printf("%d %d <---> %d %d %d\n", year, yearday, year, month, day);
	printf("\n");
	/*********************************************************************/

	/******************************* Case 3 *****************************/
	printf("Case 3:\n");
	year = 2017;
	month = 11;
	day = -22;
	yearday = 326;

	retval = day_of_year(year, month, day);
	if(retval == -1)
		printf("Incorrect input\n");
	else
		printf("%d %d %d <---> %d %d\n", year, month, day, year, retval);

	month_day(year, yearday, &month, &day);
	if(month == -1 && day == -1)
		printf("Incorrect input\n");
	else
		printf("%d %d <---> %d %d %d\n", year, yearday, year, month, day);
	printf("\n");
	/*********************************************************************/

	/******************************* Case 4 *****************************/
	printf("Case 4:\n");
	year = 2017;
	month = 11;
	day = 22;
	yearday = -326;

	retval = day_of_year(year, month, day);
	if(retval == -1)
		printf("Incorrect input\n");
	else
		printf("%d %d %d <---> %d %d\n", year, month, day, year, retval);

	month_day(year, yearday, &month, &day);
	if(month == -1 && day == -1)
		printf("Incorrect input\n");
	else
		printf("%d %d <---> %d %d %d\n", year, yearday, year, month, day);
	printf("\n");
	/*********************************************************************/

	/******************************* Case 5 *****************************/
	printf("Case 5:\n");
	year = 2017;
	month = 11;
	day = 22;
	yearday = 326;

	retval = day_of_year(year, month, day);
	if(retval == -1)
		printf("Incorrect input\n");
	else
		printf("%d %d %d <---> %d %d\n", year, month, day, year, retval);

	month_day(year, yearday, &month, &day);
	if(month == -1 && day == -1)
		printf("Incorrect input\n");
	else
		printf("%d %d <---> %d %d %d\n", year, yearday, year, month, day);
	printf("\n");
	/*********************************************************************/

	return 0;
}

/* 
The output of the above program is:
Case 1:
Incorrect input
Incorrect input

Case 2:
Incorrect input
2017 326 <---> 2017 11 22

Case 3:
Incorrect input
2017 326 <---> 2017 11 22

Case 4:
2017 11 22 <---> 2017 326
Incorrect input

Case 5:
2017 11 22 <---> 2017 326
2017 326 <---> 2017 11 22
*/

Solution by anonymous

I didn't put any error checking on the year since the leap year formula applies to pretty much all years except some distance future where the time it takes to rotate around the sun changes enough (hopefully the C language will be completely replaced by then).

#include <stdio.h>

/*
    Exercise 5-8. There is no error checking in day_of_year or month_day. Remedy this defect.
*/

int day_of_year(int year, int month, int day);
void month_day(int year, int yearday, int *pmonth, int *pday);

static char daytab[2][13] = {
    {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};

int main()
{
    int m, d;
    month_day(2021, 366, &m, &d);
    printf("%d %d\n", m, d);
    month_day(2020, 366, &m, &d); // valid
    printf("%d %d\n", m, d);
    month_day(2021, -1, &m, &d);
    printf("%d %d\n", m, d);
    month_day(2021, 0, &m, &d);
    printf("%d %d\n", m, d);
    month_day(-2020, 60, &m, &d); // valid
    printf("%d %d\n", m, d);

    printf("\n");

    printf("%d\n", day_of_year(2021, 2, 29));
    printf("%d\n", day_of_year(2020, 2, 29)); // valid
    printf("%d\n", day_of_year(2021, 1, 32));
    printf("%d\n", day_of_year(2021, 1, 31)); // valid
    printf("%d\n", day_of_year(2021, 4, 31));
    printf("%d\n", day_of_year(2021, 4, 30)); // valid
    printf("%d\n", day_of_year(2021, 0, 1));
    printf("%d\n", day_of_year(2021, -1, 1));
    printf("%d\n", day_of_year(2021, 1, 0));
    printf("%d\n", day_of_year(2021, 1, -1));
    printf("%d\n", day_of_year(2021, 13, 1));
    printf("%d\n", day_of_year(-2020, 2, 29)); // valid

    return 0;
}

// set day of year from month and day. Returns 0 if input is invalid
int day_of_year(int year, int month, int day)
{
    int leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; // if leap year, value is 1, otherwise 0 since arithmetic value of a logical expression is 0 for false and 1 for true
    if (day < 1 || month < 1 || month > 12 || (month == 2 && leap && day > 29) || (month == 2 && !leap && day > 28) || ((month == 4 || month == 6 || month == 9 || month == 11) && day > 30) || (!(month == 4 || month == 6 || month == 9 || month == 11) && day > 31))
        return 0; // invalid input
    for (int i = 1; i < month; i++)
        day += daytab[leap][i];
    return day;
}

// set month, day from day of year. Sets pmonth and pday to zero if input is invalid
void month_day(int year, int yearday, int *pmonth, int *pday)
{
    int i;
    int leap = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; // if leap year, value is 1, otherwise 0
    if (yearday < 1 || (leap && yearday > 366) || (!leap && yearday > 365))
    {
        *pmonth = *pday = 0;
        return; // invalid input
    }
    for (i = 1; yearday > daytab[leap][i]; i++)
        yearday -= daytab[leap][i];
    *pmonth = i;
    *pday = yearday;
}
Personal tools