DateTime.cpp 14 KB

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