Bare bones date_time class
#include <stdexcept>
#include <ctime>
namespace jsw {
class date_time {
public:
static date_time MIN_DATE;
static date_time MAX_DATE;
static bool is_leap_year(int year);
static int days_in_month(int year, int month);
public:
date_time(
int year = 1, int month = 1, int day = 1,
int hour = 0, int minute = 0, int second = 0);
date_time(std::time_t init, bool local = true);
date_time(const std::tm& init);
std::tm to_tm() const;
int year() const;
int month() const;
int day() const;
int weekday() const;
int hour() const;
int minute() const;
int second() const;
bool add_years(int years);
bool add_months(int months);
bool add_days(int days);
bool add_hours(int hours);
bool add_minutes(int minutes);
bool add_seconds(int seconds);
int compare(const date_time& rhs);
private:
enum {
TICKS_PER_DAY = 86400000L,
TICKS_PER_HOUR = 3600000L,
TICKS_PER_MINUTE = 60000L,
TICKS_PER_SECOND = 1000L,
DAYS_IN_400 = 146097L,
DAYS_IN_100 = 36524L,
DAYS_IN_4 = 1461L,
};
static int month_days[];
static int month_days_leap[];
static long long date_to_ticks(
int year, int month, int day,
int hour, int minute, int second);
static int date_part(long long ticks, int part);
private:
long long _ticks;
bool add_ticks(long long ticks);
};
}
namespace jsw {
/**
Public static interface
*/
date_time date_time::MIN_DATE(1, 1, 1);
date_time date_time::MAX_DATE(9999, 12, 31);
bool date_time::is_leap_year(int year)
{
if (year < 1 || year > 9999)
throw std::out_of_range("Invalid year");
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
}
int date_time::days_in_month(int year, int month)
{
int *dim = !is_leap_year(year) ? month_days : month_days_leap;
return dim[month] - dim[month - 1];
}
/**
Public instance interface
*/
date_time::date_time(
int year, int month, int day,
int hour, int minute, int second)
{
_ticks = date_to_ticks(year, month, day, hour, minute, second);
if (this != &MIN_DATE && this != &MAX_DATE) {
if (_ticks < MIN_DATE._ticks || _ticks > MAX_DATE._ticks)
throw std::invalid_argument("Unable to create date_time");
}
}
date_time::date_time(std::time_t init, bool local)
{
std::tm *cdate;
if (local)
cdate = std::localtime(&init);
else
cdate = std::gmtime(&init);
if (cdate == 0)
throw std::invalid_argument("Unable to convert time_t to date_time");
_ticks = date_to_ticks(
cdate->tm_year + 1900, cdate->tm_mon + 1, cdate->tm_mday,
cdate->tm_hour, cdate->tm_min, cdate->tm_sec);
}
date_time::date_time(const std::tm& init)
{
_ticks = date_to_ticks(
init.tm_year + 1900, init.tm_mon + 1, init.tm_mday,
init.tm_hour, init.tm_min, init.tm_sec);
if (this != &MIN_DATE && this != &MAX_DATE) {
if (_ticks < MIN_DATE._ticks || _ticks > MAX_DATE._ticks)
throw std::invalid_argument("Unable to create date_time");
}
}
tm date_time::to_tm() const
{
int *dim = !is_leap_year(year()) ? month_days : month_days_leap;
tm result;
result.tm_year = (int)(year() - 1900);
result.tm_mon = (int)(month() - 1);
result.tm_yday = (int)(dim[month()] + day());
result.tm_mday = (int)day();
result.tm_wday = (int)(weekday() - 1);
result.tm_hour = (int)hour();
result.tm_min = (int)minute();
result.tm_sec = (int)second();
result.tm_isdst = -1;
return result;
}
int date_time::year() const { return date_part(_ticks, 0); }
int date_time::month() const { return date_part(_ticks, 1); }
int date_time::day() const { return date_part(_ticks, 2); }
int date_time::weekday() const { return (_ticks / TICKS_PER_DAY + 1) % 7; }
int date_time::hour() const { return _ticks % TICKS_PER_DAY / TICKS_PER_HOUR; }
int date_time::minute() const { return _ticks % TICKS_PER_HOUR / TICKS_PER_MINUTE; }
int date_time::second() const { return _ticks % TICKS_PER_MINUTE / TICKS_PER_SECOND; }
bool date_time::add_years(int years) { return add_months(years * 12); }
bool date_time::add_months(int months)
{
int current_year = year();
int current_month = month();
int current_day = day();
int adjusted_month = current_month + months - 1;
if (adjusted_month >= 0) {
// Months are being added to the current date
current_month = adjusted_month % 12 + 1;
current_year += adjusted_month / 12;
}
else {
// Months are being subtracted from the current date
current_month = (adjusted_month + 1) % 12 + 12;
current_year += (adjusted_month - 11) / 12;
}
int year_day = days_in_month(current_year, current_month);
if (current_day > year_day)
current_day = year_day;
try {
_ticks = date_to_ticks(
current_year, current_month, current_day,
hour(), minute(), second());
return true;
} catch (std::invalid_argument&) {
return false;
}
}
bool date_time::add_days(int days)
{
return add_ticks((long long)days * TICKS_PER_DAY);
}
bool date_time::add_hours(int hours)
{
return add_ticks((long long)hours * TICKS_PER_HOUR);
}
bool date_time::add_minutes(int minutes)
{
return add_ticks((long long)minutes * TICKS_PER_MINUTE);
}
bool date_time::add_seconds(int seconds)
{
return add_ticks((long long)seconds * TICKS_PER_SECOND);
}
int date_time::compare(const date_time& rhs)
{
if (_ticks == rhs._ticks)
return 0;
return _ticks < rhs._ticks ? -1 : +1;
}
/**
Private static implementation
*/
int date_time::month_days[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
int date_time::month_days_leap[] = {0, 31, 60, 91, 121, 152, 182, 212, 244, 274, 305, 335, 366};
long long date_time::date_to_ticks(
int year, int month, int day,
int hour, int minute, int second)
{
if (year < 1 || year > 9999)
throw std::invalid_argument("Invalid year");
else if (month < 1 || month > 12)
throw std::invalid_argument("Invalid month");
int *dim = !is_leap_year(year--) ? month_days : month_days_leap;
if (day > dim[month] - dim[month - 1])
throw std::invalid_argument("Invalid day");
long long leap_days = (year / 4) - (year / 100) + (year / 400);
long long total_days = (year * 365) + leap_days + dim[month - 1] + day - 1;
if (hour > 23 || minute > 59 || second > 59)
throw std::invalid_argument("Invalid time");
return (total_days * TICKS_PER_DAY) + ((long long)hour * TICKS_PER_HOUR)
+ ((long long)minute * TICKS_PER_MINUTE) + ((long long)second * TICKS_PER_SECOND);
}
int date_time::date_part(long long ticks, int part)
{
if (part < 0 || part > 2)
throw std::invalid_argument("Invalid date part");
int result = 1; // Default to MIN_DATE parts
if (ticks != 0) {
int *dim = month_days;
long long total_days = ticks / TICKS_PER_DAY;
int span_400; // # of 400 year spans
int span_100; // # of 100 year spans
int span_4; // # of 4 year spans
int span; // # of years in the current span
span_400 = (int)(total_days / DAYS_IN_400);
total_days -= span_400 * DAYS_IN_400; // Remove all 400 year spans
span_100 = (int)(total_days / DAYS_IN_100);
// This year is a century leap year, but we don't care yet
if (span_100 == 4)
span_100 = 3;
total_days -= span_100 * DAYS_IN_100; // Remove all 100 year spans
span_4 = (int)(total_days / DAYS_IN_4);
total_days -= span_4 * DAYS_IN_4; // Remove all 4 year spans
span = (int)(total_days / 365);
// This year is a leap year, but we still don't care yet
if (span == 4)
span = 3;
total_days -= span * 365; // Remove elapsed years in the current span
// Sum up all of the span years including the current one to get the final year
int year = (span_400 * 400) + (span_100 * 100) + (span_4 * 4) + span + 1;
if (part == 0)
result = year;
else {
// *Now* we care about the leap year for matching the month day
if (is_leap_year(year))
dim = month_days_leap;
// dim has 13 elements; dim[0] will never be a match in this loop
for (int i = 1; i <= 12; i++) {
if (total_days < dim[i]) {
if (part == 1)
result = i;
else
result = (int)total_days - dim[i - 1] + 1;
break;
}
}
}
}
return result;
}
/**
Private instance implementation
*/
bool date_time::add_ticks(long long ticks)
{
long long temp = _ticks + ticks;
if (temp < MIN_DATE._ticks || temp > MAX_DATE._ticks)
return false;
_ticks = temp;
return true;
}
}
NicAx64 76 Posting Pro
Be a part of the DaniWeb community
We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.