/**
* @file artis/utils/DateTime.cpp
* @author See the AUTHORS file
*/
/*
* Copyright (C) 2012-2017 ULCO http://www.univ-littoral.fr
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#if WIN32
#include
#endif
namespace artis { namespace utils {
bool intern_isLeapYear(long year) noexcept
{
if (year % 4 != 0)
return false;
if (year % 100 != 0)
return true;
if (year % 400 != 0)
return false;
return true;
}
long intern_aYear(long year) noexcept
{
if (intern_isLeapYear(year))
return 366;
return 365;
}
long intern_aMonth(long year, long month) noexcept
{
switch (month) {
case 1: return 31;
case 2:
if (intern_isLeapYear(year))
return 29;
return 28;
case 3: return 31;
case 4: return 30;
case 5: return 31;
case 6: return 30;
case 7: return 31;
case 8: return 31;
case 9: return 30;
case 10: return 31;
case 11: return 30;
case 12: return 31;
default:
return std::numeric_limits::min();
}
}
struct intern_date
{
long myear;
long mmonth;
long mday;
long mhours;
long mminutes;
long mseconds;
intern_date() noexcept
: myear(1400)
, mmonth(1)
, mday(1)
, mhours(0)
, mminutes(0)
, mseconds(0)
{
}
intern_date(long year, long month, long day, double partofday) noexcept
: myear(year)
, mmonth(month)
, mday(day)
, mhours(0)
, mminutes(0)
, mseconds(0)
{
initPartOfDay(partofday);
}
intern_date(const intern_date& d) = default;
intern_date& operator=(const intern_date& d) = default;
intern_date(intern_date&& d) = default;
intern_date& operator=(intern_date&& d) = default;
void initPartOfDay(double partofday) noexcept //between 0 and 1
{
double f = partofday * 24.0;
mhours = std::floor(f);
f -= mhours;
f *= 60.0;
mminutes = std::floor(f);
f -= mminutes;
f *= 60.0;
mseconds = std::floor(f);
}
std::string toString(DateFormat fmt) noexcept
{
std::stringstream ss;
if (fmt != DATE_FORMAT_HMS) {
ss << myear << "-";
if (mmonth < 10) {
ss << "0";
}
ss << mmonth << "-";
if (mday < 10) {
ss << "0";
}
ss << mday ;
}
if (fmt == DATE_FORMAT_EXTENDED) {
ss << " ";
}
if (fmt != DATE_FORMAT_YMD) {
if (mhours < 10) {
ss << "0";
}
ss << mhours << ":";
if (mminutes < 10) {
ss << "0";
}
ss << mminutes << ":";
if (mseconds < 10) {
ss << "0";
}
ss << mseconds;
}
return ss.str();
}
//format : not extended = "%Y-%m-%d"
// extended = "%Y-%m-%d %H:%M:%S"
//return true if no error
bool fromString(const std::string& date, DateFormat toparse) noexcept
{
bool error = false;
if (toparse == DATE_FORMAT_EXTENDED) {
//parse "%Y-%m-%d %H:%M:%S"
try {
std::regex regex("([^\\s]+)\\s([^\\s]+)");
std::sregex_iterator next(date.begin(), date.end(), regex);
std::sregex_iterator end;
if (next != end) {
std::smatch match = *next;
if (match.size() == 3) {
fromString(match[1].str(), DATE_FORMAT_YMD);
fromString(match[2].str(), DATE_FORMAT_HMS);
} else {
error = true;
}
} else {
error =true;
}
} catch (const std::exception& e ){
error = true;
}
} else {
//parse "%Y-%m-%d" or "%H:%M:%S"
unsigned int nbmatches = 0;
try {
std::regex regex("[0-9]+|[^0-9]");
std::sregex_iterator next(date.begin(), date.end(), regex);
std::sregex_iterator end;
while (next != end) {
std::smatch match = *next;
nbmatches++;
if (nbmatches == 1) {
if (toparse == DATE_FORMAT_YMD) {
myear = std::stol(match.str());
} else {
mhours = std::stol(match.str());
}
} else if (nbmatches == 3) {
if (toparse == DATE_FORMAT_YMD) {
mmonth = std::stol(match.str());
} else {
mminutes = std::stol(match.str());
}
} else if (nbmatches == 5) {
if (toparse == DATE_FORMAT_YMD) {
mday = std::stol(match.str());
} else {
mseconds = std::stol(match.str());
}
}
next++;
}
} catch (const std::exception& e ){
error = true;
}
error = error or (nbmatches != 5);
}
return error;
}
//init from julian day eg: 2454115.05486
void fromJulianDay(double julianDay) noexcept
{
double partofday, J;
partofday = std::modf(julianDay, &J);
initPartOfDay(partofday);
//parameters for gregorian calendar (cf wikipedia)
int y=4716;
int j=1401;
int m=2;
int n=12;
int r=4;
int p=1461;
int v=3;
int u=5;
int s=153;
int w=2;
int B=274277;
int C=-38;
long f = J + j + (((4 * J + B) / 146097) * 3) / 4 + C;
long e = r * f + v;
long g = (e % p) / r;
long h = u * g + w;
mday = (h % s) / u + 1;
mmonth = ((h / s + m) % n) + 1;
myear = (e / p) - y + (n + m - mmonth) / n;
}
bool equals(const intern_date& d) const noexcept
{
return (myear == d.myear and mmonth == d.mmonth and mday == d.mday);
}
bool inf(const intern_date& d) const noexcept
{
if (myear < d.myear)
return true;
if (myear > d.myear)
return false;
if (mmonth < d.mmonth)
return true;
if (mmonth > d.mmonth)
return false;
return mday < d.mday;
}
bool sup(const intern_date& d) const noexcept
{
if (myear < d.myear)
return false;
if (myear > d.myear)
return true;
if (mmonth < d.mmonth)
return false;
if (mmonth > d.mmonth)
return true;
return mday > d.mday;
}
//tells if a date is valid
bool isValid() const noexcept
{
if (1 > mmonth or mmonth > 12)
return false;
if (1 > mday or mday > intern_aMonth(myear, mmonth))
return false;
if (0 > mhours or mhours > 23)
return false;
if (0 > mminutes or mminutes > 60)
return false;
if (0 > mseconds or mseconds > 60)
return false;
return true;
}
//add months to the current date
void addMonths(unsigned int months) noexcept
{
mmonth += months;
while (mmonth > 12) {
myear ++;
mmonth -= 12;
long nbDaysInMonth = intern_aMonth(mmonth, myear);
if (mday > nbDaysInMonth) {
mmonth ++;
mday -= nbDaysInMonth;
}
}
}
//daynNumber as computed in boost gregorian calendar.. (in wikipedia)
//12h Jan 1, 4713 BC (-4713-01-BC)
long julianDayNumber() const noexcept
{
unsigned short a = static_cast((14-mmonth)/12);
unsigned short y = static_cast(myear + 4800 - a);
unsigned short m = static_cast(mmonth + 12*a - 3);
return mday+((153*m+2)/5)+365*y+(y/4)-(y/100)+(y/400)-32045;
}
double julianDay() const noexcept
{
double res = static_cast(julianDayNumber());
res += mhours/24.0;
res += mminutes/1440.0;
res += mseconds/86400.0;
return res;
}
//day of the year (1<>366)
long dayOfyear() const noexcept
{
long ret = 0;
for (long m = 1; m < mmonth; m++)
ret += intern_aMonth(myear, m);
ret += mday;
return ret;
}
//days between a date and end of year (1<>366)
long daysUntilEndOfyear() const noexcept
{
return intern_aYear(myear) - dayOfyear();
}
//from boost date-time library
unsigned short dayOfWeek() noexcept
{
unsigned short a = static_cast((14-mmonth)/12);
unsigned short y = static_cast(myear - a);
unsigned short m = static_cast(mmonth + 12*a - 2);
unsigned short d = static_cast(
(mday + y + (y/4) - (y/100) + (y/400) + (31*m)/12) % 7);
return d;
}
//from boost date-time library
unsigned short weekOfYear() noexcept
{
unsigned long julianbegin = intern_date(myear, 1, 1, 0).julianDay();
unsigned long juliantoday = julianDay();
unsigned long day = (julianbegin + 3) % 7;
unsigned long week = (juliantoday + day - julianbegin + 4)/7;
if ((week >= 1) && (week <= 52))
return week;
if (week == 53) {
if((day==6) ||(day == 5 && intern_isLeapYear(myear))) {
return week; //under these circumstances week == 53.
} else {
return 1; //monday - wednesday is in week 1 of next year
}
}
//if the week is not in current year recalculate using the previous
//year as the beginning year
else if (week == 0) {
julianbegin = intern_date(myear-1, 1, 1, 0).julianDay();
day = (julianbegin + 3) % 7;
week = (juliantoday + day - julianbegin + 4)/7;
return week;
}
return week; //not reachable -- well except
// if day == 5 and is_leap_year != true
}
//days between a date and end of month (0<>31)
long idaysUntilEndOfMonth() const noexcept
{
return intern_aMonth(myear, mmonth)-mday;
}
//nb days between two dates (negative if this is before)
long daysUntil(const intern_date& d) const noexcept
{
if (equals(d))
return 0;
if (sup(d))
return -d.daysUntil(*this);
return d.julianDay() - julianDay();
}
};
//output format `2011-Jun-09 12:13:21'
std::string DateTime::currentDate()
{
time_t rawtime;
struct tm * timeinfo;
time (&rawtime);
timeinfo = localtime(&rawtime);
static const char mon_name[][4] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
char result[26];
sprintf(result, "%d-%.3s-%d %.2d:%.2d:%.2d \n",
1900 + timeinfo->tm_year,
mon_name[timeinfo->tm_mon],
timeinfo->tm_mday,
timeinfo->tm_hour,
timeinfo->tm_min, timeinfo->tm_sec);
return result;
}
unsigned int DateTime::year(double time)
{
intern_date d;
d.fromJulianDay(time);
return d.myear;
}
unsigned int DateTime::month(double time)
{
intern_date d;
d.fromJulianDay(time);
return d.mmonth;
}
unsigned int DateTime::dayOfMonth(double time)
{
intern_date d;
d.fromJulianDay(time);
return d.mday;
}
unsigned int DateTime::dayOfWeek(double time)
{
intern_date d;
d.fromJulianDay(time);
return d.dayOfWeek();
}
unsigned int DateTime::dayOfYear(double time)
{
intern_date d;
d.fromJulianDay(time);
return d.dayOfyear();
}
unsigned int DateTime::weekOfYear(double time)
{
intern_date d;
d.fromJulianDay(time);
return d.weekOfYear();
}
/* - - - - - - - - - - - - - --ooOoo-- - - - - - - - - - - - */
bool DateTime::isLeapYear(double time)
{
intern_date d;
d.fromJulianDay(time);
return intern_isLeapYear(d.myear);;
}
/* - - - - - - - - - - - - - --ooOoo-- - - - - - - - - - - - */
double DateTime::aYear(double time)
{
return isLeapYear(time) ? 366 : 365;
}
double DateTime::aMonth(double time)
{
intern_date d;
d.fromJulianDay(time);
return(intern_aMonth(d.myear, d.mmonth));
}
double DateTime::years(double time, unsigned int n)
{
intern_date d1;
d1.fromJulianDay(time);
intern_date d2(d1);
d2.myear += n;
return d2.julianDay() - d1.julianDay();
}
double DateTime::months(double time, unsigned int n)
{
intern_date d1;
d1.fromJulianDay(time);
intern_date d2(d1);
d2.addMonths(n);
return d2.julianDay() - d1.julianDay();
}
DateTimeUnitOptions DateTime::convertUnit(const std::string& unit)
{
if (unit == "day") {
return DATE_TIME_UNIT_DAY;
} else if (unit == "week") {
return DATE_TIME_UNIT_WEEK;
} else if (unit == "month") {
return DATE_TIME_UNIT_MONTH;
} else if (unit == "year") {
return DATE_TIME_UNIT_YEAR;
} else {
return DATE_TIME_UNIT_DAY;
}
}
double DateTime::duration(double time, double duration,
DateTimeUnitOptions unit)
{
switch (unit) {
case DATE_TIME_UNIT_NONE:
case DATE_TIME_UNIT_DAY:
return days(duration);
case DATE_TIME_UNIT_WEEK:
return weeks(duration);
case DATE_TIME_UNIT_MONTH:
return months(time, duration);
case DATE_TIME_UNIT_YEAR:
return years(time, duration);
}
return 0;
}
std::string DateTime::toJulianDayNumber(unsigned long date)
{
intern_date d;
d.fromJulianDay(static_cast(date));
return d.toString(DATE_FORMAT_YMD);
}
//parsing "2001-10-9"
long DateTime::toJulianDayNumber(const std::string& date)
{
intern_date d;
d.fromString(date, DATE_FORMAT_YMD);
return d.julianDayNumber();
}
std::string DateTime::toJulianDay(double date)
{
intern_date d;
d.fromJulianDay(date);
return d.toString(DATE_FORMAT_EXTENDED);
}
std::string DateTime::toJulianDayFmt(double date, DateFormat format){
intern_date d;
d.fromJulianDay(date);
return d.toString(format);
}
// parsing "2001-10-9 hh:mm:ss"
double DateTime::toJulianDay(const std::string& date)
{
intern_date d;
d.fromString(date, DATE_FORMAT_EXTENDED);
return d.julianDay();
}
bool DateTime::isValidYear(double date)
{
intern_date d;
d.fromJulianDay(date);
bool valid = (1399 < d.myear and d.myear < 10000);//boost rule ?
return valid;
}
double DateTime::toTime(double date, long& year,
long& month, long& day,
long& hours, long& minutes,
long& seconds)
{
intern_date d;
d.fromJulianDay(date);
year = d.myear;
month = d.mmonth;
day = d.mday;
hours = d.mhours;
minutes = d.mminutes;
seconds = d.mseconds;
return 0.0;
}
void DateTime::currentDate(long& year,
long& month,
long& day)
{
time_t rawtime;
struct tm * timeinfo;
time (&rawtime);
timeinfo = localtime (&rawtime);
year = timeinfo->tm_year;
month = timeinfo->tm_mon;
day = timeinfo->tm_mday;
}
} }