1
\$\begingroup\$

I'm implementing this date class based on <ctime> I'll appreciate your comments, critics and advice.

Please review, thank you!.

Date.h

/*
 * Date.h
 *
 * Usage:
 * Create object, and then call Set(day, month, year)
 * Throws invalid argument exception if Teb 29th is set with the wrong year (is not leap year)
 * TODO: throw invalid argument exception if date is invalid
 * Use GetDate() to get date formatted as "dd/mm/yyy"
 * Use GetAscTime() to get date formatted as "ddd mmm yy hh:mm:ss yyyy"
 * Substraction can be done between dates, it will return the differences in days
 *  i.e. Date today - Date yesterday = 1
 * TODO: overload + operator as Date + int
 * TODO: overload - operator as Date - int
 */
#include <ctime>
#include <string>

#ifndef DATE_H_
#define DATE_H_

class Date
{
public:
    Date();
    void Set(int day, int month, int year);
    int operator - (const Date& rhs);
    const char* GetDate() const;
    const char* GetAscTime() const;
    time_t GetDateValue() const;

private:
    std::time_t ttTime;
    std::tm tmTime;
    mutable char szTimeBuffer[255];
    static constexpr int nSecPerDay = 60 * 60 * 24;
};

#endif /* DATE_H_ */

Date.cpp

/*
 * Date.cpp
 */
#include "Date.h"
#include <stdexcept>

Date::Date()
    : ttTime(0), tmTime({0}), szTimeBuffer("")
{
    std::time(&ttTime);
    localtime_s(&tmTime, &ttTime);
}

void Date::Set(int day, int month, int year)
{
    tmTime.tm_mday = day;
    tmTime.tm_mon = month - 1;
    tmTime.tm_year = year - 1900;

    ttTime = std::mktime(&tmTime);

    // Temp date constructed to check if Feb 29th for the specified year exists
    // checks if the value of 29/Feb/YYYY is the same as 01/Mar/YYYY
    // if true, means that year is not leap year and should not have Feb 29th
    // throws invalid argument exception

    std::tm tmTemp = {0};
    std::time_t ttTemp = 0;

    // Specify march 1st, use same year as input
    tmTemp.tm_mday = 1;
    tmTemp.tm_mon = month;
    tmTemp.tm_year = tmTime.tm_year;

    ttTemp = std::mktime(&tmTemp);

    if(ttTemp == ttTime)
    {
        std::string errMsg("Error!, Feb 29th doesn't exist in year ");
        errMsg.append(std::to_string(tmTime.tm_year + 1900));
        errMsg.append("!!");
        throw( std::invalid_argument(errMsg) );
    }

}

int Date::operator -(const Date& rhs)
{
    return (std::difftime(ttTime, rhs.ttTime) / nSecPerDay);
}

const char* Date::GetDate() const
{
    std::strftime(szTimeBuffer, 255, "%d/%m/%y", &tmTime);
    return szTimeBuffer;
}

const char* Date::GetAscTime() const
{
    asctime_s(szTimeBuffer, 255, &tmTime);
    return szTimeBuffer;
}

time_t Date::GetDateValue() const
{
    return ttTime;
}
\$\endgroup\$
  • \$\begingroup\$ Usually, nSecPerDay = 60 * 60 * 24 - but not when there's a leap second! \$\endgroup\$ – Toby Speight Oct 23 '19 at 15:41
  • \$\begingroup\$ Well you got me in a trouble there :D. As far as I've read, there's 1 leap second every year either at June 30th or Dec 31st. So, I can probably make it float and add 0.0027f (1 second divided by 365 [366 if is leap year]) to nSecPerDay to compensate the leap second. As keeping track of dates instead of time, won't be much of a difference, at least for 86400 years, isn't it? :( \$\endgroup\$ – Vital Zero Oct 23 '19 at 20:31
3
\$\begingroup\$

A few things I see:

The subtraction(-) operator would make more sense to return a Date object instead of an int. This would also fit in with standard practice. If such a function is needed, I would suggest naming a specific function for that,DiffDays perhaps. This has the advantage being more intuitive for the user.

Instead of requiring each instance of the Date object to require the user calling Set, it would be more intuitive to overload the constructor.

It seems to me that returning a string instead of a char* would be more in fitting with a c++ program.

\$\endgroup\$
  • \$\begingroup\$ Thanks!, you're right. I just overloaded the + operator, returning a Date, but for "-", I didn't see it that way. I had the constructor overloaded, but had to remove it temporarily because couldn't use it outside try{} scope. But I will add it again. std::string was coming next (thats why I included <string>" :D. Thank you for your replies, they are very useful to me. \$\endgroup\$ – Vital Zero Oct 22 '19 at 21:24
  • \$\begingroup\$ I would say that that subtraction ought to return a Duration object of some sort (and we should be able to add and subtract durations to/from dates and to/from each other). \$\endgroup\$ – Toby Speight Oct 23 '19 at 15:42
  • \$\begingroup\$ Duration as in std::chrono::duration?. if that so, then it would change the whole purpose of the class, which is date (no time). or what other kind of duration object are you refering to?. thanks for the suggestion, BTW :) \$\endgroup\$ – Vital Zero Oct 23 '19 at 20:15
0
\$\begingroup\$

Ok, this is the updated class. Added your suggestions

Main.h

/*
 * Date.h
 *
 * Usage:
 * Create object, and then call Set(day, month, year)
 * Throws invalid argument exception if Teb 29th is set with the wrong year (is not leap year)
 * TODO: throw invalid argument exception if date is invalid
 * Use GetDate() to get date formatted as "dd/mm/yyyy"
 * Use GetAscTime() to get date formatted as "ddd mmm yy hh:mm:ss yyyy"
 * Substraction can be done between dates with DateDiff(), it will return the differences in days
 *  i.e. Date today - Date yesterday = 1
 */
#include <ctime>
#include <string>

#ifndef DATE_H_
#define DATE_H_

class Date
{
public:
    Date();
    Date(int day, int month, int year);
    void Set(int day, int month, int year);
    std::string GetDate() const;
    std::string GetAscTime() const;
    std::string GetUTC();
    int DateDiff(const Date& rhs);
    time_t GetDateValue() const;
    bool operator == (const Date& rhs);
    Date operator + (int days);
    Date operator - (int days);

private:
    std::time_t ttTime;
    std::tm tmTime;
    mutable char szTimeBuffer[255];
    static constexpr int nSecPerDay = 60 * 60 * 24;
};

#endif /* DATE_H_ */

Main.cpp

/*
 * Date.cpp
 */
#include "Date.h"
#include <stdexcept>

Date::Date()
    : ttTime(0), tmTime({0}), szTimeBuffer("")
{
    std::time(&ttTime);
    localtime_s(&tmTime, &ttTime);
}

Date::Date(int day, int month, int year)
    : ttTime(0), tmTime({0}), szTimeBuffer("")
{
    Set(day, month, year);
}

void Date::Set(int day, int month, int year)
{
    tmTime.tm_mday = day;
    tmTime.tm_mon = month - 1;
    tmTime.tm_year = year - 1900;

    ttTime = std::mktime(&tmTime);

    // Temp date constructed to check if Feb 29th for the specified year exists
    // checks if the value of 29/Feb/YYYY is the same as 01/Mar/YYYY
    // if true, means that year is not leap year and should not have Feb 29th
    // throws invalid argument exception

    std::tm tmTemp = {0};
    std::time_t ttTemp = 0;

    // Specify march 1st, use same year as input
    tmTemp.tm_mday = 1;
    tmTemp.tm_mon = month;
    tmTemp.tm_year = tmTime.tm_year;

    ttTemp = std::mktime(&tmTemp);

    if(ttTemp == ttTime)
    {
        std::string errMsg("Error!, Feb 29th doesn't exist in year ");
        errMsg.append(std::to_string(tmTime.tm_year + 1900));
        errMsg.append("!!");
        throw( std::invalid_argument(errMsg) );
    }

}

int Date::DateDiff(const Date& rhs)
{
    return (std::difftime(ttTime, rhs.ttTime) / nSecPerDay);
}

std::string Date::GetDate() const
{
    std::strftime(szTimeBuffer, 255, "%d/%m/%y", &tmTime);
    return std::string(szTimeBuffer);
}

std::string Date::GetAscTime() const
{
    asctime_s(szTimeBuffer, 255, &tmTime);
    return std::string(szTimeBuffer);
}

time_t Date::GetDateValue() const
{
    return ttTime;
}

bool Date::operator ==(const Date &rhs)
{
    return (
        (tmTime.tm_year == rhs.tmTime.tm_year) &&
        (tmTime.tm_mday == rhs.tmTime.tm_mday) &&
        (tmTime.tm_mon == rhs.tmTime.tm_mon)
    );
}

Date Date::operator +(int days)
{
    std::tm tmTemp = {0};
    std::time_t ttTemp = ttTime + (days * nSecPerDay);
    localtime_s(&tmTemp, &ttTemp);
    Date dTemp(tmTemp.tm_mday, tmTemp.tm_mon + 1, tmTemp.tm_year + 1900);
    return dTemp;
}

std::string Date::GetUTC()
{
    std::tm tmTemp = {0};
    gmtime_s(&tmTemp, &ttTime);
    std::strftime(szTimeBuffer, 255, "%c %z", &tmTemp);

    return std::string(szTimeBuffer);
}

Date Date::operator -(int days)
{
    return operator+(-days);
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.