/****************************************************************************** * K&R Exercise 5-8 * * Program to get the day of year from the year, month and day * or the month and day from the year and day of year, taking into * account leap years, complete with rejection of invalid possibilities. * (I went a little nuts trying to make sure the program would work * well no matter what invalid input is provided). * * 15 November 2024 (day 320 ; ) * * ~yetimach * ******************************************************************************/ #include #include #define LEAP_MAX_DAYS 366 #define NONLEAP_MAX_DAYS 365 #define REASONABLE_YEAR 10000 #define YEAR_MAX_PlACES 5 #define MONTH_MAX_PLACES 2 #define MONTH_DAY_MAX_PLACES 2 #define YEAR_DAY_MAX_PLACES 3 #define SELECTION_MAX_PLACES 1 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 get_selection(int max_places){ int num, i, c; num = i = 0; while (!isdigit(c = getchar()) && c != '\n') ; if (c == '\n') return num; if (isdigit(c)){ num = c - '0'; ++i; } while (isdigit(c = getchar()) && c != '\n' && i < max_places){ num = (num * 10) + (c - '0'); ++i; } if (c != '\n') while (getchar() != '\n') ; return num; } /* read a positive number of a given length, max_places, ignoring all * non-digit input and returning 0 if no input is provided * Special feature: conserve '\n' allows a return pressed with input * lacking to be passed through. 1 indicates to save '\n' in int c; * 0 indicates to change it to 0 in order to read a new number. */ int get_positive_int_doy(int max_places, int conserve_nl){ int num, i; static int c = 0; i = num = 0; if (!conserve_nl) c = 0; if (c == '\n') return num; while (!isdigit(c = getchar()) && c != '\n') ; if (c == '\n') return num; if (isdigit(c)){ num = c - '0'; ++i; } while (isdigit(c = getchar()) && c != '\n' && i < max_places){ num = (num * 10) + (c - '0'); ++i; } return num; } int day_of_year(int year, int month, int day){ int i, leap; if (year < 0 || year > REASONABLE_YEAR || month < 1 || month > 12) return -1; leap = (year%4 == 0 && year%100 != 0) || year%400 == 0; if (day < 1 || day > daytab[leap][month]) return -1; for (i = 1; i < month; i++) day += daytab[leap][i]; return day; } char *ordinal_ending(int n){ int ending; static char *endings_array[] = { "st", "nd", "rd", "th" }; if (n < 0) n = -n; if (n > 3 && n < 21) ending = 3; else{ n %= 10; switch (n) { case 1: ending = 0; break; case 2: ending = 1; break; case 3: ending = 2; break; default: ending = 3; break; } } return endings_array[ending]; } int process_day_of_year(void){ int year, month, day, yearday = 0; year = get_positive_int_doy(YEAR_MAX_PlACES, 0); month = get_positive_int_doy(MONTH_MAX_PLACES, 1); day = get_positive_int_doy(MONTH_DAY_MAX_PLACES, 1); yearday = day_of_year(year, month, day); if (yearday != -1) printf("\nThat is the %d%s day of that year.\n", yearday, ordinal_ending(yearday)); return yearday; } int month_day(int year, int yearday, int *pmonth, int *pday){ int i, leap; if (year < 0 || year > REASONABLE_YEAR || yearday < 1) return -1; leap = (year%4 == 0 && year%100 != 0) || year%400 == 0; int max_days = (leap) ? LEAP_MAX_DAYS : NONLEAP_MAX_DAYS; if (yearday > max_days) return -1; for (i = 1; yearday > daytab[leap][i]; i++) yearday -= daytab[leap][i]; *pmonth = i; *pday = yearday; return 0; } char *month_name(int n){ static char *name[] = { "Illegal month", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; return (n < 1 || n > 12) ? name[0] : name[n]; } int process_month_day(void){ int dm, m; int *dmp, *mp; dmp = &dm; mp = &m; int year, yearday = 0; year = get_positive_int_doy(YEAR_MAX_PlACES, 0); yearday = get_positive_int_doy(YEAR_DAY_MAX_PLACES, 1); yearday = month_day(year, yearday, mp, dmp); if (yearday != -1) printf("\nThat is %s %d%s.\n", month_name(*mp), *dmp, ordinal_ending(*dmp)); return yearday; } int main(void) { printf("Year Day Program\n\n" "For day of year from month and day of month, enter 1.\n" "For month and day of month from year and day of year, enter 2.\n" "Your selection: "); int selection, tries, valid = 1; tries = selection = 0; do { if (!valid){ if (tries >= 3) return 0; printf("Invalid option. Try again: "); } selection = get_selection(SELECTION_MAX_PLACES); if (selection != 1 && selection != 2){ valid = 0; } ++tries; }while (selection != 1 && selection != 2); tries = 0; if (selection == 1){ printf("\nEnter year followed by month number and month day.\n" "Example: 2010 7 15\n" "year month day: "); while (tries < 3 && (valid = process_day_of_year()) == -1){ if (valid == -1 && tries < 2) printf("Invalid option. Try again: "); ++tries; } } else { printf("\nEnter year followed day of year.\n" "Example: 2010 130\n" "year and day of year: "); while (tries < 3 && (valid = process_month_day()) == -1){ if (valid == -1 && tries < 2) printf("Invalid option. Try again: "); ++tries; } } return 0; }