DateTime.cpp 14 KB

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