Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Operator Overloading Implementation of a C++ Date Class

Tech 1

Date Comparison Operators

Equality check:

bool Date::operator==(const Date& other) const
{
    return year_ == other.year_ 
        && month_ == other.month_ 
        && day_ == other.day_;
}

Inequality check can reuse the equality implemantation:

bool Date::operator!=(const Date& other) const
{
    return !(*this == other);
}

Less than comparison:

bool Date::operator<(const Date& other) const
{
    if (year_ != other.year_) {
        return year_ < other.year_;
    }
    if (month_ != other.month_) {
        return month_ < other.month_;
    }
    return day_ < other.day_;
}

Less than or equal comparison:

bool Date::operator<=(const Date& other) const
{
    return *this < other || *this == other;
}

All remaining comparison operators (>, >=) can be derived by reusing the existing implementations:

bool Date::operator>(const Date& other) const
{
    return !(*this <= other);
}

bool Date::operator>=(const Date& other) const
{
    return !(*this < other);
}

Helper Function: Get Days in Month

We use a static lookup array to store standard month days, and add special handling for leap year February:

int Date::getMonthDays(int year, int month) const
{
    assert(month > 0 && month < 13);
    static int daysPerMonth[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    
    if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) {
        return 29;
    }
    return daysPerMonth[month];
}

Add Days to a Date

We implement += first that modifies the current object, then + creates a copy and reuses += to avoid modifying the original date:

Date& Date::operator+=(int addDays)
{
    day_ += addDays;
    while (day_ > getMonthDays(year_, month_)) {
        day_ -= getMonthDays(year_, month_);
        month_++;
        if (month_ > 12) {
            year_++;
            month_ = 1;
        }
    }
    return *this;
}

Date Date::operator+(int addDays) const
{
    Date temp = *this;
    temp += addDays;
    return temp;
}

Subtract Days from a Date

Similar to adding days, we use the same pattern of implementing -= firrst then reusing it for -:

Date& Date::operator-=(int subDays)
{
    day_ -= subDays;
    while (day_ <= 0) {
        month_--;
        if (month_ < 1) {
            year_--;
            month_ = 12;
        }
        day_ += getMonthDays(year_, month_);
    }
    return *this;
}

Date Date::operator-(int subDays) const
{
    Date temp = *this;
    temp -= subDays;
    return temp;
}

Prefix and Postfix Increment/Decrement

To distinguish prefix and postfix overloads, postfix operators accept a dummy int parameter:

// Prefix increment: ++date
Date& Date::operator++()
{
    *this += 1;
    return *this;
}

// Postfix increment: date++
Date Date::operator++(int)
{
    Date temp = *this;
    *this += 1;
    return temp;
}

// Prefix decrement: --date
Date& Date::operator--()
{
    *this -= 1;
    return *this;
}

// Postfix decrement: date--
Date Date::operator--(int)
{
    Date temp = *this;
    *this -= 1;
    return temp;
}

Calculate Days Between Two Dates

Count the number of days by incrementing the smaller date until it matches the larger date:

int Date::operator-(const Date& other) const
{
    int resultSign = 1;
    Date larger = *this;
    Date smaller = other;

    if (*this < other) {
        resultSign = -1;
        larger = other;
        smaller = *this;
    }

    int dayCount = 0;
    while (smaller != larger) {
        ++smaller;
        ++dayCount;
    }

    return dayCount * resultSign;
}

Date Validation

Check if a constructed date is valid:

bool Date::isValid() const
{
    if (year_ <= 0 || month_ < 1 || month_ > 12) {
        return false;
    }
    return day_ >= 1 && day_ <= getMonthDays(year_, month_);
}

Stream Operator Overloading

Stream insertion (<<) and extraction (>>) cannot be member functions (since the left operand is the stream object), so we implement them as non-member functions with friend access to private members, and return stream references to support chaining:

Add friend declarations inside the Date class:

friend ostream& operator<<(ostream& out, const Date& date);
friend istream& operator>>(istream& in, Date& date);

Implementation of stream ensertion:

ostream& operator<<(ostream& out, const Date& date)
{
    out << date.year_ << "/" << date.month_ << "/" << date.day_ << endl;
    return out;
}

Implementation of stream extraction with input validation:

istream& operator>>(istream& in, Date& date)
{
    while (true) {
        in >> date.year_ >> date.month_ >> date.day_;
        if (date.isValid()) {
            break;
        }
        cout << "Invalid date entered, please re-enter: " << endl;
    }
    return in;
}

Full Header File (Date.h)

#pragma once
#include <iostream>
#include <cassert>
using namespace std;

class Date
{
public:
    Date(int year = 1, int month = 1, int day = 1);
    bool isValid() const;

    // Comparison operators
    bool operator<(const Date& other) const;
    bool operator<=(const Date& other) const;
    bool operator>(const Date& other) const;
    bool operator>=(const Date& other) const;
    bool operator==(const Date& other) const;
    bool operator!=(const Date& other) const;

    // Arithmetic operators
    Date& operator+=(int addDays);
    Date operator+(int addDays) const;
    Date& operator-=(int subDays);
    Date operator-(int subDays) const;

    // Increment/decrement
    Date& operator++();
    Date operator++(int);
    Date& operator--();
    Date operator--(int);

    // Days between two dates
    int operator-(const Date& other) const;

private:
    int getMonthDays(int year, int month) const;
    int year_;
    int month_;
    int day_;

    friend ostream& operator<<(ostream& out, const Date& d);
    friend istream& operator>>(istream& in, Date& d);
};

ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);

Full Source File (Date.cpp)

#include "Date.h"

Date::Date(int year, int month, int day)
{
    year_ = year;
    month_ = month;
    day_ = day;

    if (!isValid()) {
        cout << "Warning: Constructed invalid date" << endl;
    }
}

bool Date::isValid() const
{
    if (year_ <= 0 || month_ < 1 || month_ > 12) {
        return false;
    }
    return day_ >= 1 && day_ <= getMonthDays(year_, month_);
}

int Date::getMonthDays(int year, int month) const
{
    assert(month > 0 && month < 13);
    static int daysMap[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))) {
        return 29;
    }
    return daysMap[month];
}

bool Date::operator<(const Date& other) const
{
    if (year_ != other.year_) {
        return year_ < other.year_;
    }
    if (month_ != other.month_) {
        return month_ < other.month_;
    }
    return day_ < other.day_;
}

bool Date::operator<=(const Date& other) const
{
    return *this < other || *this == other;
}

bool Date::operator>(const Date& other) const
{
    return !(*this <= other);
}

bool Date::operator>=(const Date& other) const
{
    return !(*this < other);
}

bool Date::operator==(const Date& other) const
{
    return year_ == other.year_ && month_ == other.month_ && day_ == other.day_;
}

bool Date::operator!=(const Date& other) const
{
    return !(*this == other);
}

Date& Date::operator+=(int addDays)
{
    day_ += addDays;
    while (day_ > getMonthDays(year_, month_)) {
        day_ -= getMonthDays(year_, month_);
        month_++;
        if (month_ > 12) {
            year_++;
            month_ = 1;
        }
    }
    return *this;
}

Date Date::operator+(int addDays) const
{
    Date temp = *this;
    temp += addDays;
    return temp;
}

Date& Date::operator-=(int subDays)
{
    day_ -= subDays;
    while (day_ <= 0) {
        month_--;
        if (month_ < 1) {
            year_--;
            month_ = 12;
        }
        day_ += getMonthDays(year_, month_);
    }
    return *this;
}

Date Date::operator-(int subDays) const
{
    Date temp = *this;
    temp -= subDays;
    return temp;
}

Date& Date::operator++()
{
    *this += 1;
    return *this;
}

Date Date::operator++(int)
{
    Date temp = *this;
    *this += 1;
    return temp;
}

Date& Date::operator--()
{
    *this -= 1;
    return *this;
}

Date Date::operator--(int)
{
    Date temp = *this;
    *this -= 1;
    return temp;
}

int Date::operator-(const Date& other) const
{
    int resultSign = 1;
    Date maxDate = *this;
    Date minDate = other;

    if (*this < other) {
        resultSign = -1;
        maxDate = other;
        minDate = *this;
    }

    int daysBetween = 0;
    while (minDate != maxDate) {
        ++minDate;
        ++daysBetween;
    }

    return daysBetween * resultSign;
}

ostream& operator<<(ostream& out, const Date& date)
{
    out << date.year_ << "年" << date.month_ << "月" << date.day_ << "日" << endl;
    return out;
}

istream& operator>>(istream& in, Date& date)
{
    while (true) {
        cout << "Enter year month day: ";
        in >> date.year_ >> date.month_ >> date.day_;
        if (date.isValid()) {
            break;
        }
        cout << "Invalid date, please try again" << endl;
    }
    return in;
}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.