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; }