123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722 |
- /**
- * @file artis/utils/DateTime.cpp
- * @author See the AUTHORS file
- */
- /*
- * This file is part of VLE, a framework for multi-modeling, simulation
- * and analysis of complex dynamical systems.
- * https://www.vle-project.org
- *
- * Copyright (c) 2003-2019 Gauthier Quesnel <gauthier.quesnel@inra.fr>
- * Copyright (c) 2003-2019 ULCO http://www.univ-littoral.fr
- * Copyright (c) 2007-2019 INRA http://www.inra.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 <regex>
- #include <sstream>
- #include <cmath>
- #include <ctime>
- namespace artis {
- namespace utils {
- bool
- intern_isLeapYear(int year) noexcept
- {
- if (year % 4 != 0)
- return false;
- if (year % 100 != 0)
- return true;
- if (year % 400 != 0)
- return false;
- return true;
- }
- int
- intern_aYear(int year) noexcept
- {
- if (intern_isLeapYear(year))
- return 366;
- return 365;
- }
- int
- intern_aMonth(int year, int 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<int>::min();
- }
- }
- struct intern_date {
- int myear{1400};
- int mmonth{1};
- int mday{1};
- int mhours{0};
- int mminutes{0};
- int mseconds{0};
- enum STR_FORMAT {
- extended, //"%Y-%m-%d %H:%M:%S"
- ymd, //"%Y-%m-%d"
- hms //"%H:%M:%S"
- };
- intern_date() noexcept = default;
- intern_date(int year, int month, int 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 = static_cast<int>(std::floor(f));
- f -= std::floor(f);
- f *= 60.0;
- mminutes = static_cast<int>(std::floor(f));
- f -= std::floor(f);
- f *= 60.0;
- mseconds = static_cast<int>(std::floor(f));
- }
- std::string toString(STR_FORMAT fmt) noexcept
- {
- std::stringstream ss;
- if (fmt != hms) {
- ss << myear << "-";
- if (mmonth < 10) {
- ss << "0";
- }
- ss << mmonth << "-";
- if (mday < 10) {
- ss << "0";
- }
- ss << mday;
- }
- if (fmt == extended) {
- ss << " ";
- }
- if (fmt != 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();
- }
- 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, STR_FORMAT toparse) noexcept
- {
- bool error = false;
- if (toparse == extended) {
- // parse "%Y-%m-%d %H:%M:%S"
- try {
- std::regex regex(R"(([^\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(), ymd);
- fromString(match[2].str(), hms);
- } else {
- error = true;
- }
- } else {
- error = true;
- }
- }
- catch (const std::exception& e) {
- error = true;
- }
- } else {
- // parse "%Y-%m-%d" or "%H:%M:%S"
- 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 == ymd) {
- myear = std::stoi(match.str());
- } else {
- mhours = std::stoi(match.str());
- }
- } else if (nbmatches == 3) {
- if (toparse == ymd) {
- mmonth = std::stoi(match.str());
- } else {
- mminutes = std::stoi(match.str());
- }
- } else if (nbmatches == 5) {
- if (toparse == ymd) {
- mday = std::stoi(match.str());
- } else {
- mseconds = std::stoi(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;
- auto f =
- static_cast<int>(J + j + (((4 * J + B) / 146097) * 3) / 4 + C);
- int e = r * f + v;
- int g = (e % p) / r;
- int 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(int months) noexcept
- {
- mmonth += months;
- while (mmonth > 12) {
- myear++;
- mmonth -= 12;
- int 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)
- int julianDayNumber() const noexcept
- {
- auto a = static_cast<int>((14 - mmonth) / 12);
- auto y = static_cast<int>(myear + 4800 - a);
- auto m = static_cast<int>(mmonth + 12 * a - 3);
- return mday + ((153 * m + 2) / 5) + 365 * y + (y / 4) - (y / 100) +
- (y / 400) - 32045;
- }
- double julianDay() const noexcept
- {
- auto res = static_cast<double>(julianDayNumber());
- res += static_cast<double>(mhours) / 24.0;
- res += static_cast<double>(mminutes) / 1440.0;
- res += static_cast<double>(mseconds) / 86400.0;
- return res;
- }
- // day of the year (1<>366)
- int dayOfyear() const noexcept
- {
- int ret = 0;
- for (int m = 1; m < mmonth; m++)
- ret += intern_aMonth(myear, m);
- ret += mday;
- return ret;
- }
- // days between a date and end of year (1<>366)
- int daysUntilEndOfyear() const noexcept
- {
- return intern_aYear(myear) - dayOfyear();
- }
- // from boost date-time library
- int dayOfWeek() noexcept
- {
- auto a = static_cast<int>((14 - mmonth) / 12);
- auto y = static_cast<int>(myear - a);
- auto m = static_cast<int>(mmonth + 12 * a - 2);
- auto d = static_cast<int>(
- (mday + y + (y / 4) - (y / 100) + (y / 400) + (31 * m) / 12) % 7);
- return d;
- }
- // from boost date-time library
- int weekOfYear() noexcept
- {
- auto julianbegin =
- static_cast<int>(intern_date(myear, 1, 1, 0).julianDay());
- auto juliantoday = static_cast<int>(julianDay());
- int day = (julianbegin + 3) % 7;
- int week = (juliantoday + day - julianbegin + 4) / 7;
- if ((week >= 1) && (week <= 52))
- return static_cast<short>(week);
- if (week == 53) {
- if ((day == 6) || (day == 5 && intern_isLeapYear(myear))) {
- return static_cast<short>(
- 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 =
- static_cast<int>(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)
- int idaysUntilEndOfMonth() const noexcept
- {
- return intern_aMonth(myear, mmonth) - mday;
- }
- // nb days between two dates (negative if this is before)
- int daysUntil(const intern_date& d) const noexcept
- {
- if (equals(d))
- return 0;
- if (sup(d))
- return -d.daysUntil(*this);
- return static_cast<int>(d.julianDay() - julianDay());
- }
- };
- // output format `2011-Jun-09 12:13:21'
- std::string DateTime::currentDate()
- {
- static const char mon_name[][4] = {"Jan", "Feb", "Mar", "Apr",
- "May", "Jun", "Jul", "Aug",
- "Sep", "Oct", "Nov", "Dec"};
- time_t rawtime;
- struct tm* timeinfo;
- time(&rawtime);
- timeinfo = localtime(&rawtime);
- 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(static_cast<int>(duration));
- case DATE_TIME_UNIT_WEEK:
- return weeks(static_cast<int>(duration));
- case DATE_TIME_UNIT_MONTH:
- return months(time, static_cast<int>(duration));
- case DATE_TIME_UNIT_YEAR:
- return years(time, static_cast<int>(duration));
- }
- return 0;
- }
- std::string DateTime::toJulianDayNumber(int date)
- {
- intern_date d;
- d.fromJulianDay(static_cast<double>(date));
- return d.toString(intern_date::ymd);
- }
- // parsing "2001-10-9"
- long int DateTime::toJulianDayNumber(const std::string& date)
- {
- intern_date d;
- d.fromString(date, intern_date::ymd);
- return d.julianDayNumber();
- }
- std::string DateTime::toJulianDay(double date)
- {
- intern_date d;
- d.fromJulianDay(date);
- return d.toString(intern_date::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"
- // if error, then try parsing "2001-10-9"
- double DateTime::toJulianDay(const std::string& date)
- {
- intern_date d;
- bool error = d.fromString(date, intern_date::extended);
- if (error) {
- d.fromString(date, intern_date::ymd);
- }
- 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,
- int& year,
- int& month,
- int& day,
- int& hours,
- int& minutes,
- int& 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(int& year, int& month, int& day)
- {
- time_t rawtime;
- struct tm* timeinfo;
- time(&rawtime);
- timeinfo = localtime(&rawtime);
- year = timeinfo->tm_year;
- month = timeinfo->tm_mon;
- day = timeinfo->tm_mday;
- }
- }
- }
|