I see a number of things that could help you improve your code.
Don't abuse using namespace std
Putting using namespace std
at the top of every program is a bad habit that you'd do well to avoid.
Choose better function names
There are two very similar names organize()
and organizeInfo()
and it's not obvious without reading the code what the difference might be.
Reconsider the constructor
I found this constructor counterintuitive:
date dawg(cin);
It would have been much easier to understand if it had been written like this:
date dawg;
cin >> dawg;
Use const where practical
Your outputInfo()
member fuction does not alter the underlying date
, so it should be declared const
. In general, whenever you are writing a variable or function prototype look for places you can use const
.
Write object I/O functions as inserters and extractors
Instead of a member function outputInfo
, write an extractor instead:
friend ostream &operator<<(std::ostream &out, const date& d) {
return out << d.year << " " << d.month << " " << d.day;
}
Similarly, instead of the constructor taking a reference to a std::istream
, use a inserter:
friend istream& operator>>(std::istream &in, date& d) {
return in >> d.year >> d.month >> d.day;
}
Fix the prompt string
It may seem like a minor issue but the user interface is a very important part of most programs because it's the only part that a user would see. For that reason instead of "and ill try and organize" which soundw like the program is not well, it should be "and I'll try ..."
Use a better algorithm
Right now, if I enter the 10 March 2010 date as 3 10 2010
which is the ordering commonly used in the United States, the program reinterprets that as 3 October 2010. (This was intended to make the same point as @πάντα ῥεῖ in the section "Don't promise things to users of your API, you cannot ever guarantee" but that answer states the point more accurately and eloquently.)
Use better default values
The default constructor creates a date of 0 0 0
which is not really a valid date in most calendrical systems. Better would be to assign a default value which, while possibly not meaningful, would at least be a valid date.
Use true
and false
for boolean values
Since you're using C++, you should use the keywords true
and false
instead of 1
and 0
for boolean return values.
Only #include
things that are needed
The code currently has this line:
#include <string>
However, there is no code in this program that needs anything from that header, so it should be omitted.
Do more error checking
Dates such as 30 February 2015 could be detected as invalid and rejected, as by throw
ing an exception.
Use available library functions
Instead of switchItems()
, you could use std::swap
if you're using C++11 or newer.
Eliminate return 0
at the end of main
When a C++ program reaches the end of main
the compiler will automatically generate code to return 0, so there is no reason to put return 0;
explicitly at the end of main
.
An alternative approach
As others have correctly noted, some dates are inherently ambiguous and can't always be correctly interpreted. A date of '4 5 1980' might be intended to be 4 May or 5 April. However, any date with day > 12
and year > 31
(which is most dates in the past 2000 years) can be unambiguously rearranged. Further, dates that are invalid, such as 31 April (there are only 30 days in April) can be rejected. Here's an alternative approach that includes a bool ambiguous
data member. Leap years are partially handled. The idea is that if the given date is unambiguous, no further action is needed, but an ambiguous date might require, for example, that the user is asked further questions about intent. Here's the refactored code:
#include <iostream>
#include <algorithm>
#include <cassert>
class Date
{
public:
Date(unsigned y=1970, unsigned m=1, unsigned d=1) :
year{y},
month{m},
day{d},
ambiguous{true}
{ rearrange(); }
bool operator==(const Date& other) const {
return year == other.year && month == other.month && day == other.day;
}
bool isAmbiguous() const { return ambiguous; }
friend std::istream& operator>>(std::istream &in, Date& d) {
in >> d.year >> d.month >> d.day;
d.rearrange();
return in;
}
friend std::ostream &operator<<(std::ostream &out, const Date& d) {
return out << d.year << " " << d.month << " " << d.day;
}
private:
bool rearrange();
static bool check(unsigned y, unsigned m, unsigned d);
static bool invalid(unsigned y, unsigned m, unsigned d);
unsigned year;
unsigned month;
unsigned day;
bool ambiguous;
};
bool Date::invalid(unsigned y, unsigned m, unsigned d) {
return y == 0 || m == 0 || d == 0;
}
bool Date::check(unsigned y, unsigned m, unsigned d)
{
switch (m) {
case 1: // January
case 3: // March
case 5: // May
case 7: // July
case 8: // August
case 10: // October
case 12: // December
return d <= 31;
case 4: // April
case 6: // June
case 9: // September
case 11: // November
return d <= 30;
case 2: // February
if (y % 4 != 0)
return d <= 28;
return d <= 29;
default:
return false;
}
}
// returns true if the resulting, possibly rearranged Date is valid
bool Date::rearrange()
{
// if it's invalid, no rearranging can fix it
if (invalid(year, month, day)) {
return false;
}
if (day > 31) {
std::swap(day, year);
}
if (month > 31) {
std::swap(month, year);
}
if (month > 12) {
std::swap(month, day);
}
// is it already good?
if (check(year, month, day)) {
ambiguous = !((year > 31) && ((month == day) || (day > 12)));
return true;
}
ambiguous = true;
return false;
}
void test()
{
Date anchor{1969, 7, 20};
std::cout << "anchor : " << anchor << "\n";
Date ymd{1969, 7, 20};
assert(ymd == anchor);
Date ydm{1969, 20, 7};
assert(ydm == anchor);
Date myd{7, 1969, 20};
assert(myd == anchor);
Date mdy{7, 20, 1969};
assert(mdy == anchor);
Date dmy{20, 7, 1969};
assert(dmy == anchor);
Date dym{20, 1969, 7};
assert(dym == anchor);
}
int main()
{
std::cout << "Enter year, month, Date and I'll try and organize it" << std::endl;
Date dawg;
test();
std::cin >> dawg;
std::cout << dawg << " is" << (dawg.isAmbiguous() ? "" : " not") << " ambiguous.\n";
}