#include <iostream>
#include <string>
#include <vector>
#include <optional>
#include <sstream>
/*
Compare two dates in C++
------------------------
This program demonstrates how to compare two dates using only the C++ standard library.
It uses a modular design, clear comments, and a full test suite.
Concepts:
- Parsing dates safely
- Representing dates with a simple struct
- Comparing dates lexicographically (year → month → day)
- Handling invalid formats
- Edge‑case testing
Architecture notes:
- A dedicated function handles parsing.
- Another function performs comparison.
- Main runs multiple predefined test cases.
- No external dependencies; only standard library.
Performance notes:
- Comparisons are O(1).
- Parsing is fast and predictable.
- Memory usage is minimal.
Pitfalls:
- Invalid date strings must be handled.
- Comparing raw strings is unsafe; always convert to a structured type.
- No timezone logic here; this is pure date comparison.
*/
struct Date {
int year{};
int month{};
int day{};
};
/**
* Safely parse a date string in the format YYYY-MM-DD.
*
* Error handling:
* - Returns std::nullopt if the date is invalid.
*/
std::optional<Date> parseDate(const std::string& s) {
std::istringstream iss(s);
Date d;
char dash1{}, dash2{};
if (!(iss >> d.year >> dash1 >> d.month >> dash2 >> d.day)) {
return std::nullopt;
}
if (dash1 != '-' || dash2 != '-') {
return std::nullopt;
}
// Basic validation
if (d.month < 1 || d.month > 12) return std::nullopt;
if (d.day < 1 || d.day > 31) return std::nullopt; // Simplified; not checking month lengths
return d;
}
/**
* Compare two Date objects.
*
* Returns:
* - "earlier"
* - "later"
* - "equal"
*/
std::string compareDates(const Date& a, const Date& b) {
if (a.year < b.year) return "earlier";
if (a.year > b.year) return "later";
if (a.month < b.month) return "earlier";
if (a.month > b.month) return "later";
if (a.day < b.day) return "earlier";
if (a.day > b.day) return "later";
return "equal";
}
/**
* Run a single test case:
* - Parse both dates
* - Handle invalid input
* - Compare if valid
*/
std::tuple<std::string, std::string, std::string>
runTestCase(const std::string& d1, const std::string& d2) {
auto p1 = parseDate(d1);
auto p2 = parseDate(d2);
if (!p1 || !p2) {
return {d1, d2, "invalid date format"};
}
return {d1, d2, compareDates(*p1, *p2)};
}
/**
* Main test suite:
* - Multiple test cases
* - Includes edge cases
* - Prints results cleanly
*/
int main() {
std::cout << "Date comparison tests:\n\n";
std::vector<std::pair<std::string, std::string>> tests = {
{"2024-01-01", "2024-01-02"}, // earlier
{"2024-01-02", "2024-01-01"}, // later
{"2024-01-01", "2024-01-01"}, // equal
{"1999-12-31", "2000-01-01"}, // millennium boundary
{"2024-02-29", "2024-03-01"}, // leap year (not validated strictly)
{"2024-02-29", "2023-02-28"}, // leap vs non-leap
{"2024-13-01", "2024-01-01"}, // invalid month
{"2024-00-10", "2024-01-01"}, // invalid month
{"2024-01-32", "2024-01-01"}, // invalid day
{"abcd-ef-gh", "2024-01-01"}, // invalid format
{"2024-01-01", "abcd-ef-gh"}, // invalid format
};
for (const auto& [d1, d2] : tests) {
auto [s1, s2, result] = runTestCase(d1, d2);
std::cout << "Compare '" << s1 << "' vs '" << s2 << "' → " << result << "\n";
}
}
/*
run:
Date comparison tests:
Compare '2024-01-01' vs '2024-01-02' → earlier
Compare '2024-01-02' vs '2024-01-01' → later
Compare '2024-01-01' vs '2024-01-01' → equal
Compare '1999-12-31' vs '2000-01-01' → earlier
Compare '2024-02-29' vs '2024-03-01' → earlier
Compare '2024-02-29' vs '2023-02-28' → later
Compare '2024-13-01' vs '2024-01-01' → invalid date format
Compare '2024-00-10' vs '2024-01-01' → invalid date format
Compare '2024-01-32' vs '2024-01-01' → invalid date format
Compare 'abcd-ef-gh' vs '2024-01-01' → invalid date format
Compare '2024-01-01' vs 'abcd-ef-gh' → invalid date format
*/