123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673 |
- /**
- * @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 <http://www.gnu.org/licenses/>.
- */
- #include <artis/utils/DateTime.hpp>
- #include <sstream>
- #include <regex>
- #include <ctime>
- #include <cmath>
- #if WIN32
- #include <iso646.h>
- #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<long>::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<unsigned short>((14-mmonth)/12);
- unsigned short y = static_cast<unsigned short>(myear + 4800 - a);
- unsigned short m = static_cast<unsigned short>(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<double>(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<unsigned short>((14-mmonth)/12);
- unsigned short y = static_cast<unsigned short>(myear - a);
- unsigned short m = static_cast<unsigned short>(mmonth + 12*a - 2);
- unsigned short d = static_cast<unsigned short>(
- (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<double>(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;
- }
- } }
|