1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
#include <iostream>
#include <string>
#include <sstream>
struct Date
{
int year;
int month;
int day;
};
// convenience operator to see if one date is before/after another
bool operator >= (const Date& l, const Date& r)
{
if(l.year > r.year) return true;
if(l.year < r.year) return false;
if(l.month > r.month) return true;
if(l.month < r.month) return false;
return (l.day >= r.day);
}
bool isLeapYear(int year)
{
if(year < 0) return false; // <- can't have negative months wtf
// years not divisible by 4 are never leap years
if(year % 4) return false;
// years divisible by 400 are always leap years
if(!(year % 400)) return true;
// other years divisible by 100 are never leap years
if(!(year % 100)) return false;
// if we made it here, this is a leap year
return true;
}
int daysInMonth(int month, int year)
{
if(month >= 12) return 0; // 0 days in non-existent months
// February is stupid
if(month == 1)
{
if(isLeapYear(year))
return 29;
else
return 28;
}
// all other months can be a lut
static const int lut[12] = {31,0,31,30,31,30,31,31,30,31,30,31};
return lut[month];
}
int daysInPreviousMonth(int month, int year)
{
if(month == 0)
return daysInMonth(11,year-1);
else
return daysInMonth(month-1,year);
}
bool parseDate(const std::string& str, Date& date)
{
std::stringstream s(str);
// date is in the form mm/dd/yyyy
// I hate parsing text. I kind of suck at it too. This will work but is pretty ugly:
if(!(s >> date.month)) return false;
if(s.get() != '/') return false;
if(!(s >> date.day)) return false;
if(s.get() != '/') return false;
if(!(s >> date.year)) return false;
// offset days and months by 1 (we want January to be 0, not 1. 1-based is stupid)
date.month -= 1;
date.day -= 1;
// months must be between 0-11
if(date.month < 0) return false;
if(date.month >= 12) return false;
// and let's say the year must be > 1, just to prevent some wrapping errors
if(date.year <= 1) return false;
// days must be less than the number of days in this month
if(date.day >= daysInMonth(date.month,date.year)) return false;
if(date.day < 0) return false;
// if all that checks out, we have a valid date
return true;
}
bool getDateFromUser(const char* message, Date& date)
{
std::string str;
// print our message to the user
std::cout << message;
// loop until we get valid input
while(true)
{
std::getline(std::cin,str);
if(str.empty()) return false; // user wants to exit
if(parseDate(str,date)) return true; // user gave us valid input
// otherwise, invalid input. Keep looping
std::cout << "Invalid input.\n" << message;
}
}
Date calcDifference(const Date& birth, const Date& current)
{
Date result;
result.day = current.day - birth.day;
result.month = current.month - birth.month;
result.year = current.year - birth.year;
// if we have negative days, borrow from the previous month
if(result.day < 0)
{
result.month -= 1;
result.day += daysInPreviousMonth(current.month,current.year);
}
// if we have negative months, borrow from the previous year
if(result.month < 0)
{
result.year -= 1;
result.month += 12;
}
return result;
}
// a stupid little function to give a 's' suffix for numbers other than 1
const char* suffix(int num)
{
if(num == 1) return "";
else return "s";
}
int main()
{
Date birth, current, age;
std::cout << "Age calculator. Input an empty line to quit.\n\n";
// loop until user wants to quit
while(true)
{
// get the birth date
if(!getDateFromUser("Input your birth date (mm/dd/yyyy): ",birth)) return 0; // return (quit) if they input nothing
if(!getDateFromUser("Input the current date (mm/dd/yyyy): ",current)) return 0; // return (quit) if they input nothing
// make sure the current date is after the birth date
if(current >= birth)
{
age = calcDifference(birth,current);
std::cout << "\nYou are "
<< age.year << " year" << suffix(age.year) << ", "
<< age.month << " month" << suffix(age.month) << ", and "
<< age.day << " day" << suffix(age.day) << " old.\n\n";
}
else
{
std::cout << "Your birth date must be after the current date. Please try again.\n\n";
}
}
}
|