DateTime.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. /**
  2. * @file artis/utils/DateTime.cpp
  3. * @author See the AUTHORS file
  4. */
  5. /*
  6. * This file is part of VLE, a framework for multi-modeling, simulation
  7. * and analysis of complex dynamical systems.
  8. * https://www.vle-project.org
  9. *
  10. * Copyright (c) 2003-2019 Gauthier Quesnel <gauthier.quesnel@inra.fr>
  11. * Copyright (c) 2003-2019 ULCO http://www.univ-littoral.fr
  12. * Copyright (c) 2007-2019 INRA http://www.inra.fr
  13. *
  14. * This program is free software: you can redistribute it and/or modify
  15. * it under the terms of the GNU General Public License as published by
  16. * the Free Software Foundation, either version 3 of the License, or
  17. * (at your option) any later version.
  18. *
  19. * This program is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. * GNU General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU General Public License
  25. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  26. */
  27. #include <artis/utils/DateTime.hpp>
  28. #include <regex>
  29. #include <sstream>
  30. #include <cmath>
  31. #include <ctime>
  32. namespace artis {
  33. namespace utils {
  34. bool
  35. intern_isLeapYear(int year) noexcept
  36. {
  37. if (year % 4 != 0)
  38. return false;
  39. if (year % 100 != 0)
  40. return true;
  41. if (year % 400 != 0)
  42. return false;
  43. return true;
  44. }
  45. int
  46. intern_aYear(int year) noexcept
  47. {
  48. if (intern_isLeapYear(year))
  49. return 366;
  50. return 365;
  51. }
  52. int
  53. intern_aMonth(int year, int month) noexcept
  54. {
  55. switch (month) {
  56. case 1:
  57. return 31;
  58. case 2:
  59. if (intern_isLeapYear(year))
  60. return 29;
  61. return 28;
  62. case 3:
  63. return 31;
  64. case 4:
  65. return 30;
  66. case 5:
  67. return 31;
  68. case 6:
  69. return 30;
  70. case 7:
  71. return 31;
  72. case 8:
  73. return 31;
  74. case 9:
  75. return 30;
  76. case 10:
  77. return 31;
  78. case 11:
  79. return 30;
  80. case 12:
  81. return 31;
  82. default:
  83. return std::numeric_limits<int>::min();
  84. }
  85. }
  86. struct intern_date {
  87. int myear{1400};
  88. int mmonth{1};
  89. int mday{1};
  90. int mhours{0};
  91. int mminutes{0};
  92. int mseconds{0};
  93. enum STR_FORMAT {
  94. extended, //"%Y-%m-%d %H:%M:%S"
  95. ymd, //"%Y-%m-%d"
  96. hms //"%H:%M:%S"
  97. };
  98. intern_date() noexcept = default;
  99. intern_date(int year, int month, int day, double partofday) noexcept
  100. :myear(year), mmonth(month), mday(day), mhours(0), mminutes(0), mseconds(0)
  101. {
  102. initPartOfDay(partofday);
  103. }
  104. intern_date(const intern_date& d) = default;
  105. intern_date& operator=(const intern_date& d) = default;
  106. intern_date(intern_date&& d) = default;
  107. intern_date& operator=(intern_date&& d) = default;
  108. void initPartOfDay(double partofday) noexcept // between 0 and 1
  109. {
  110. double f = partofday * 24.0;
  111. mhours = static_cast<int>(std::floor(f));
  112. f -= std::floor(f);
  113. f *= 60.0;
  114. mminutes = static_cast<int>(std::floor(f));
  115. f -= std::floor(f);
  116. f *= 60.0;
  117. mseconds = static_cast<int>(std::floor(f));
  118. }
  119. std::string toString(STR_FORMAT fmt) noexcept
  120. {
  121. std::stringstream ss;
  122. if (fmt != hms) {
  123. ss << myear << "-";
  124. if (mmonth < 10) {
  125. ss << "0";
  126. }
  127. ss << mmonth << "-";
  128. if (mday < 10) {
  129. ss << "0";
  130. }
  131. ss << mday;
  132. }
  133. if (fmt == extended) {
  134. ss << " ";
  135. }
  136. if (fmt != ymd) {
  137. if (mhours < 10) {
  138. ss << "0";
  139. }
  140. ss << mhours << ":";
  141. if (mminutes < 10) {
  142. ss << "0";
  143. }
  144. ss << mminutes << ":";
  145. if (mseconds < 10) {
  146. ss << "0";
  147. }
  148. ss << mseconds;
  149. }
  150. return ss.str();
  151. }
  152. std::string toString(DateFormat fmt) noexcept
  153. {
  154. std::stringstream ss;
  155. if (fmt != DATE_FORMAT_HMS) {
  156. ss << myear << "-";
  157. if (mmonth < 10) {
  158. ss << "0";
  159. }
  160. ss << mmonth << "-";
  161. if (mday < 10) {
  162. ss << "0";
  163. }
  164. ss << mday;
  165. }
  166. if (fmt == DATE_FORMAT_EXTENDED) {
  167. ss << " ";
  168. }
  169. if (fmt != DATE_FORMAT_YMD) {
  170. if (mhours < 10) {
  171. ss << "0";
  172. }
  173. ss << mhours << ":";
  174. if (mminutes < 10) {
  175. ss << "0";
  176. }
  177. ss << mminutes << ":";
  178. if (mseconds < 10) {
  179. ss << "0";
  180. }
  181. ss << mseconds;
  182. }
  183. return ss.str();
  184. }
  185. // format : not extended = "%Y-%m-%d"
  186. // extended = "%Y-%m-%d %H:%M:%S"
  187. // return true if no error
  188. bool fromString(const std::string& date, STR_FORMAT toparse) noexcept
  189. {
  190. bool error = false;
  191. if (toparse == extended) {
  192. // parse "%Y-%m-%d %H:%M:%S"
  193. try {
  194. std::regex regex(R"(([^\s]+)\s([^\s]+))");
  195. std::sregex_iterator next(date.begin(), date.end(), regex);
  196. std::sregex_iterator end;
  197. if (next != end) {
  198. std::smatch match = *next;
  199. if (match.size() == 3) {
  200. fromString(match[1].str(), ymd);
  201. fromString(match[2].str(), hms);
  202. } else {
  203. error = true;
  204. }
  205. } else {
  206. error = true;
  207. }
  208. }
  209. catch (const std::exception& e) {
  210. error = true;
  211. }
  212. } else {
  213. // parse "%Y-%m-%d" or "%H:%M:%S"
  214. int nbmatches = 0;
  215. try {
  216. std::regex regex("[0-9]+|[^0-9]");
  217. std::sregex_iterator next(date.begin(), date.end(), regex);
  218. std::sregex_iterator end;
  219. while (next != end) {
  220. std::smatch match = *next;
  221. nbmatches++;
  222. if (nbmatches == 1) {
  223. if (toparse == ymd) {
  224. myear = std::stoi(match.str());
  225. } else {
  226. mhours = std::stoi(match.str());
  227. }
  228. } else if (nbmatches == 3) {
  229. if (toparse == ymd) {
  230. mmonth = std::stoi(match.str());
  231. } else {
  232. mminutes = std::stoi(match.str());
  233. }
  234. } else if (nbmatches == 5) {
  235. if (toparse == ymd) {
  236. mday = std::stoi(match.str());
  237. } else {
  238. mseconds = std::stoi(match.str());
  239. }
  240. }
  241. next++;
  242. }
  243. }
  244. catch (const std::exception& e) {
  245. error = true;
  246. }
  247. error = error or (nbmatches != 5);
  248. }
  249. return error;
  250. }
  251. // init from julian day eg: 2454115.05486
  252. void fromJulianDay(double julianDay) noexcept
  253. {
  254. double partofday, J;
  255. partofday = std::modf(julianDay, &J);
  256. initPartOfDay(partofday);
  257. // parameters for gregorian calendar (cf wikipedia)
  258. int y = 4716;
  259. int j = 1401;
  260. int m = 2;
  261. int n = 12;
  262. int r = 4;
  263. int p = 1461;
  264. int v = 3;
  265. int u = 5;
  266. int s = 153;
  267. int w = 2;
  268. int B = 274277;
  269. int C = -38;
  270. auto f =
  271. static_cast<int>(J + j + (((4 * J + B) / 146097) * 3) / 4 + C);
  272. int e = r * f + v;
  273. int g = (e % p) / r;
  274. int h = u * g + w;
  275. mday = (h % s) / u + 1;
  276. mmonth = ((h / s + m) % n) + 1;
  277. myear = (e / p) - y + (n + m - mmonth) / n;
  278. }
  279. bool equals(const intern_date& d) const noexcept
  280. {
  281. return (myear == d.myear and mmonth == d.mmonth and mday == d.mday);
  282. }
  283. bool inf(const intern_date& d) const noexcept
  284. {
  285. if (myear < d.myear)
  286. return true;
  287. if (myear > d.myear)
  288. return false;
  289. if (mmonth < d.mmonth)
  290. return true;
  291. if (mmonth > d.mmonth)
  292. return false;
  293. return mday < d.mday;
  294. }
  295. bool sup(const intern_date& d) const noexcept
  296. {
  297. if (myear < d.myear)
  298. return false;
  299. if (myear > d.myear)
  300. return true;
  301. if (mmonth < d.mmonth)
  302. return false;
  303. if (mmonth > d.mmonth)
  304. return true;
  305. return mday > d.mday;
  306. }
  307. // tells if a date is valid
  308. bool isValid() const noexcept
  309. {
  310. if (1 > mmonth or mmonth > 12)
  311. return false;
  312. if (1 > mday or mday > intern_aMonth(myear, mmonth))
  313. return false;
  314. if (0 > mhours or mhours > 23)
  315. return false;
  316. if (0 > mminutes or mminutes > 60)
  317. return false;
  318. if (0 > mseconds or mseconds > 60)
  319. return false;
  320. return true;
  321. }
  322. // add months to the current date
  323. void addMonths(int months) noexcept
  324. {
  325. mmonth += months;
  326. while (mmonth > 12) {
  327. myear++;
  328. mmonth -= 12;
  329. int nbDaysInMonth = intern_aMonth(mmonth, myear);
  330. if (mday > nbDaysInMonth) {
  331. mmonth++;
  332. mday -= nbDaysInMonth;
  333. }
  334. }
  335. }
  336. // daynNumber as computed in boost gregorian calendar.. (in wikipedia)
  337. // 12h Jan 1, 4713 BC (-4713-01-BC)
  338. int julianDayNumber() const noexcept
  339. {
  340. auto a = static_cast<int>((14 - mmonth) / 12);
  341. auto y = static_cast<int>(myear + 4800 - a);
  342. auto m = static_cast<int>(mmonth + 12 * a - 3);
  343. return mday + ((153 * m + 2) / 5) + 365 * y + (y / 4) - (y / 100) +
  344. (y / 400) - 32045;
  345. }
  346. double julianDay() const noexcept
  347. {
  348. auto res = static_cast<double>(julianDayNumber());
  349. res += static_cast<double>(mhours) / 24.0;
  350. res += static_cast<double>(mminutes) / 1440.0;
  351. res += static_cast<double>(mseconds) / 86400.0;
  352. return res;
  353. }
  354. // day of the year (1<>366)
  355. int dayOfyear() const noexcept
  356. {
  357. int ret = 0;
  358. for (int m = 1; m < mmonth; m++)
  359. ret += intern_aMonth(myear, m);
  360. ret += mday;
  361. return ret;
  362. }
  363. // days between a date and end of year (1<>366)
  364. int daysUntilEndOfyear() const noexcept
  365. {
  366. return intern_aYear(myear) - dayOfyear();
  367. }
  368. // from boost date-time library
  369. int dayOfWeek() noexcept
  370. {
  371. auto a = static_cast<int>((14 - mmonth) / 12);
  372. auto y = static_cast<int>(myear - a);
  373. auto m = static_cast<int>(mmonth + 12 * a - 2);
  374. auto d = static_cast<int>(
  375. (mday + y + (y / 4) - (y / 100) + (y / 400) + (31 * m) / 12) % 7);
  376. return d;
  377. }
  378. // from boost date-time library
  379. int weekOfYear() noexcept
  380. {
  381. auto julianbegin =
  382. static_cast<int>(intern_date(myear, 1, 1, 0).julianDay());
  383. auto juliantoday = static_cast<int>(julianDay());
  384. int day = (julianbegin + 3) % 7;
  385. int week = (juliantoday + day - julianbegin + 4) / 7;
  386. if ((week >= 1) && (week <= 52))
  387. return static_cast<short>(week);
  388. if (week == 53) {
  389. if ((day == 6) || (day == 5 && intern_isLeapYear(myear))) {
  390. return static_cast<short>(
  391. week); // under these circumstances week == 53.
  392. } else {
  393. return 1; // monday - wednesday is in week 1 of next year
  394. }
  395. }
  396. // if the week is not in current year recalculate using the previous
  397. // year as the beginning year
  398. else if (week == 0) {
  399. julianbegin =
  400. static_cast<int>(intern_date(myear - 1, 1, 1, 0).julianDay());
  401. day = (julianbegin + 3) % 7;
  402. week = (juliantoday + day - julianbegin + 4) / 7;
  403. return week;
  404. }
  405. return week; // not reachable -- well except
  406. // if day == 5 and is_leap_year != true
  407. }
  408. // days between a date and end of month (0<>31)
  409. int idaysUntilEndOfMonth() const noexcept
  410. {
  411. return intern_aMonth(myear, mmonth) - mday;
  412. }
  413. // nb days between two dates (negative if this is before)
  414. int daysUntil(const intern_date& d) const noexcept
  415. {
  416. if (equals(d))
  417. return 0;
  418. if (sup(d))
  419. return -d.daysUntil(*this);
  420. return static_cast<int>(d.julianDay() - julianDay());
  421. }
  422. };
  423. // output format `2011-Jun-09 12:13:21'
  424. std::string DateTime::currentDate()
  425. {
  426. static const char mon_name[][4] = {"Jan", "Feb", "Mar", "Apr",
  427. "May", "Jun", "Jul", "Aug",
  428. "Sep", "Oct", "Nov", "Dec"};
  429. time_t rawtime;
  430. struct tm* timeinfo;
  431. time(&rawtime);
  432. timeinfo = localtime(&rawtime);
  433. char result[26];
  434. sprintf(result, "%d-%.3s-%d %.2d:%.2d:%.2d \n",
  435. 1900 + timeinfo->tm_year,
  436. mon_name[timeinfo->tm_mon],
  437. timeinfo->tm_mday,
  438. timeinfo->tm_hour,
  439. timeinfo->tm_min, timeinfo->tm_sec);
  440. return result;
  441. }
  442. unsigned int DateTime::year(double time)
  443. {
  444. intern_date d;
  445. d.fromJulianDay(time);
  446. return d.myear;
  447. }
  448. unsigned int DateTime::month(double time)
  449. {
  450. intern_date d;
  451. d.fromJulianDay(time);
  452. return d.mmonth;
  453. }
  454. unsigned int DateTime::dayOfMonth(double time)
  455. {
  456. intern_date d;
  457. d.fromJulianDay(time);
  458. return d.mday;
  459. }
  460. unsigned int DateTime::dayOfWeek(double time)
  461. {
  462. intern_date d;
  463. d.fromJulianDay(time);
  464. return d.dayOfWeek();
  465. }
  466. unsigned int DateTime::dayOfYear(double time)
  467. {
  468. intern_date d;
  469. d.fromJulianDay(time);
  470. return d.dayOfyear();
  471. }
  472. unsigned int DateTime::weekOfYear(double time)
  473. {
  474. intern_date d;
  475. d.fromJulianDay(time);
  476. return d.weekOfYear();
  477. }
  478. /* - - - - - - - - - - - - - --ooOoo-- - - - - - - - - - - - */
  479. bool DateTime::isLeapYear(double time)
  480. {
  481. intern_date d;
  482. d.fromJulianDay(time);
  483. return intern_isLeapYear(d.myear);;
  484. }
  485. /* - - - - - - - - - - - - - --ooOoo-- - - - - - - - - - - - */
  486. double DateTime::aYear(double time)
  487. {
  488. return isLeapYear(time) ? 366 : 365;
  489. }
  490. double DateTime::aMonth(double time)
  491. {
  492. intern_date d;
  493. d.fromJulianDay(time);
  494. return (intern_aMonth(d.myear, d.mmonth));
  495. }
  496. double DateTime::years(double time, unsigned int n)
  497. {
  498. intern_date d1;
  499. d1.fromJulianDay(time);
  500. intern_date d2(d1);
  501. d2.myear += n;
  502. return d2.julianDay() - d1.julianDay();
  503. }
  504. double DateTime::months(double time, unsigned int n)
  505. {
  506. intern_date d1;
  507. d1.fromJulianDay(time);
  508. intern_date d2(d1);
  509. d2.addMonths(n);
  510. return d2.julianDay() - d1.julianDay();
  511. }
  512. DateTimeUnitOptions DateTime::convertUnit(const std::string& unit)
  513. {
  514. if (unit == "day") {
  515. return DATE_TIME_UNIT_DAY;
  516. } else if (unit == "week") {
  517. return DATE_TIME_UNIT_WEEK;
  518. } else if (unit == "month") {
  519. return DATE_TIME_UNIT_MONTH;
  520. } else if (unit == "year") {
  521. return DATE_TIME_UNIT_YEAR;
  522. } else {
  523. return DATE_TIME_UNIT_DAY;
  524. }
  525. }
  526. double DateTime::duration(double time, double duration, DateTimeUnitOptions unit)
  527. {
  528. switch (unit) {
  529. case DATE_TIME_UNIT_NONE:
  530. case DATE_TIME_UNIT_DAY:
  531. return days(static_cast<int>(duration));
  532. case DATE_TIME_UNIT_WEEK:
  533. return weeks(static_cast<int>(duration));
  534. case DATE_TIME_UNIT_MONTH:
  535. return months(time, static_cast<int>(duration));
  536. case DATE_TIME_UNIT_YEAR:
  537. return years(time, static_cast<int>(duration));
  538. }
  539. return 0;
  540. }
  541. std::string DateTime::toJulianDayNumber(int date)
  542. {
  543. intern_date d;
  544. d.fromJulianDay(static_cast<double>(date));
  545. return d.toString(intern_date::ymd);
  546. }
  547. // parsing "2001-10-9"
  548. long int DateTime::toJulianDayNumber(const std::string& date)
  549. {
  550. intern_date d;
  551. d.fromString(date, intern_date::ymd);
  552. return d.julianDayNumber();
  553. }
  554. std::string DateTime::toJulianDay(double date)
  555. {
  556. intern_date d;
  557. d.fromJulianDay(date);
  558. return d.toString(intern_date::extended);
  559. }
  560. std::string DateTime::toJulianDayFmt(double date, DateFormat format)
  561. {
  562. intern_date d;
  563. d.fromJulianDay(date);
  564. return d.toString(format);
  565. }
  566. // parsing "2001-10-9 hh:mm:ss"
  567. // if error, then try parsing "2001-10-9"
  568. double DateTime::toJulianDay(const std::string& date)
  569. {
  570. intern_date d;
  571. bool error = d.fromString(date, intern_date::extended);
  572. if (error) {
  573. d.fromString(date, intern_date::ymd);
  574. }
  575. return d.julianDay();
  576. }
  577. bool DateTime::isValidYear(double date)
  578. {
  579. intern_date d;
  580. d.fromJulianDay(date);
  581. bool valid = (1399 < d.myear and d.myear < 10000); // boost rule ?
  582. return valid;
  583. }
  584. double DateTime::toTime(double date,
  585. int& year,
  586. int& month,
  587. int& day,
  588. int& hours,
  589. int& minutes,
  590. int& seconds)
  591. {
  592. intern_date d;
  593. d.fromJulianDay(date);
  594. year = d.myear;
  595. month = d.mmonth;
  596. day = d.mday;
  597. hours = d.mhours;
  598. minutes = d.mminutes;
  599. seconds = d.mseconds;
  600. return 0.0;
  601. }
  602. void DateTime::currentDate(int& year, int& month, int& day)
  603. {
  604. time_t rawtime;
  605. struct tm* timeinfo;
  606. time(&rawtime);
  607. timeinfo = localtime(&rawtime);
  608. year = timeinfo->tm_year;
  609. month = timeinfo->tm_mon;
  610. day = timeinfo->tm_mday;
  611. }
  612. }
  613. }