#include "DateRel.h"
#include <panda/from_chars.h>
#include <ostream>
namespace panda { namespace date {
const DateRel YEAR (1);
const DateRel MONTH (0,1);
const DateRel WEEK (0,0,7);
const DateRel DAY (0,0,1);
const DateRel HOUR (0,0,0,1);
const DateRel MIN (0,0,0,0,1);
const DateRel SEC (0,0,0,0,0,1);
static inline void relstr_val (char*& ptr, ptime_t val, char units) {
auto res = to_chars(ptr, ptr+20, val);
assert(!res.ec);
ptr = res.ptr;
*(ptr++) = units;
}
void DateRel::set (const Date& from_date, const Date& till_date) {
_sec = _min = _hour = _day = _month = _year = 0;
_from = from_date;
if (from_date.timezone() != till_date.timezone() && from_date.gmtoff() != till_date.gmtoff()) {
_from->to_timezone(till_date.timezone());
}
bool reverse = from_date > till_date;
auto& from = reverse ? till_date.date() : _from->date();
auto& till = reverse ? _from->date() : till_date.date();
_sec = till.sec - from.sec;
if (_sec < 0) { _sec += 60; _min--; }
_min += till.min - from.min;
if (_min < 0) { _min += 60; _hour--; }
_hour += till.hour - from.hour;
if (_hour < 0) { _hour += 24; _day--; }
_day += till.mday - from.mday;
if (_day < 0) {
int tmpy = till.year;
int tmpm = till.mon-1;
if (tmpm < 0) { tmpm += 12; tmpy--; }
int days = panda::time::days_in_month(tmpy, tmpm);
_day += days;
_month--;
}
_month += till.mon - from.mon;
if (_month < 0) { _month += 12; _year--; }
_year += till.year - from.year;
if (reverse) negate();
}
string DateRel::to_string (Format fmt) const {
char buf[150];
char* ptr = buf;
switch (fmt) {
case Format::simple:
if (_year ) { relstr_val(ptr, _year, 'Y'); }
if (_month) { if (ptr != buf) *(ptr++) = ' '; relstr_val(ptr, _month, 'M'); }
if (_day ) { if (ptr != buf) *(ptr++) = ' '; relstr_val(ptr, _day, 'D'); }
if (_hour ) { if (ptr != buf) *(ptr++) = ' '; relstr_val(ptr, _hour, 'h'); }
if (_min ) { if (ptr != buf) *(ptr++) = ' '; relstr_val(ptr, _min, 'm'); }
if (_sec ) { if (ptr != buf) *(ptr++) = ' '; relstr_val(ptr, _sec, 's'); }
break;
case Format::iso8601i:
if (_from) {
auto dstr = _from->to_string(Date::Format::iso8601);
auto len = dstr.length();
memcpy(ptr, dstr.data(), len);
ptr += len;
*ptr++ = '/';
}
// fall through
case Format::iso8601d:
*ptr++ = 'P';
if (_year ) { relstr_val(ptr, _year, 'Y'); }
if (_month) { relstr_val(ptr, _month, 'M'); }
if (_day ) { relstr_val(ptr, _day, 'D'); }
if (_hour | _min | _sec) *ptr++ = 'T';
if (_hour ) { relstr_val(ptr, _hour, 'H'); }
if (_min ) { relstr_val(ptr, _min, 'M'); }
if (_sec ) { relstr_val(ptr, _sec, 'S'); }
break;
default: throw std::invalid_argument("unknown format type for relative date output");
}
return string(buf, ptr - buf);
}
DateRel& DateRel::operator+= (const DateRel& op) {
_sec += op._sec;
_min += op._min;
_hour += op._hour;
_day += op._day;
_month += op._month;
_year += op._year;
return *this;
}
DateRel& DateRel::operator-= (const DateRel& op) {
_sec -= op._sec;
_min -= op._min;
_hour -= op._hour;
_day -= op._day;
_month -= op._month;
_year -= op._year;
return *this;
}
DateRel& DateRel::operator*= (double koef) {
if (fabs(koef) < 1 && koef != 0) return operator/=(1/koef);
_sec *= koef;
_min *= koef;
_hour *= koef;
_day *= koef;
_month *= koef;
_year *= koef;
return *this;
}
DateRel& DateRel::operator/= (double koef) {
if (fabs(koef) <= 1) return operator*=(1/koef);
double td;
int64_t tmp;
tmp = _year;
_year /= koef;
td = (tmp - _year*koef)*12;
tmp = td;
_month += tmp;
td = (td - tmp)*((double)2629744/86400);
tmp = td;
_day += tmp;
td = (td - tmp)*24;
tmp = td;
_hour += tmp;
td = (td - tmp)*60;
tmp = td;
_min += tmp;
td = (td - tmp)*60;
_sec += td;
tmp = _month;
_month /= koef;
td = (tmp - _month*koef)*((double)2629744/86400);
tmp = td;
_day += tmp;
td = (td - tmp)*24;
tmp = td;
_hour += tmp;
td = (td - tmp)*60;
tmp = td;
_min += tmp;
td = (td - tmp)*60;
_sec += td;
tmp = _day;
_day /= koef;
td = (tmp - _day*koef)*24;
tmp = td;
_hour += tmp;
td = (td - tmp)*60;
tmp = td;
_min += tmp;
td = (td - tmp)*60;
_sec += td;
tmp = _hour;
_hour /= koef;
td = (tmp - _hour*koef)*60;
tmp = td;
_min += tmp;
td = (td - tmp)*60;
_sec += td;
tmp = _min;
_min /= koef;
_sec += (tmp - _min*koef)*60;
_sec /= koef;
return *this;
}
DateRel& DateRel::negate () {
_sec = -_sec;
_min = -_min;
_hour = -_hour;
_day = -_day;
_month = -_month;
_year = -_year;
return *this;
}
std::ostream& operator<< (std::ostream& os, const DateRel& rel) {
return os << rel.to_string();
}
}}