The Battle for Wesnoth  1.15.3+dev
date_time.cpp
Go to the documentation of this file.
1 //
2 // M A R I A D B + +
3 //
4 // Copyright Sylvain Rochette Langlois 2013,
5 // Frantisek Boranek 2015,
6 // The ViaDuck Project 2016 - 2020.
7 // Distributed under the Boost Software License, Version 1.0.
8 // (See accompanying file LICENSE or copy at
9 // http://www.boost.org/LICENSE_1_0.txt)
10 
11 #include <mysql.h>
12 #include <sstream>
13 #include <mariadb++/exceptions.hpp>
14 #include <mariadb++/date_time.hpp>
16 #include <iomanip>
17 #include <chrono>
18 #include "private.hpp"
19 
20 using namespace mariadb;
21 
22 namespace {
23 const u8 g_month_lengths[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
24 }
25 
26 #define MS_PER_SEC 1000
27 #define MS_PER_MIN (MS_PER_SEC * 60)
28 #define MS_PER_HOUR (MS_PER_MIN * 60)
29 #define MS_PER_DAY (MS_PER_HOUR * 24)
30 
31 date_time::date_time(u16 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond) : time() {
33 }
34 
36  set(dt.year(), dt.month(), dt.day(), dt.hour(), dt.minute(), dt.second(), dt.millisecond());
37 }
38 
40  set(1900, 1, 1, t.hour(), t.minute(), t.second(), t.millisecond());
41 }
42 
43 date_time::date_time(const tm& t) : time() {
44  set(t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, 0);
45 }
46 
47 date_time::date_time(const time_t& time) : mariadb::time() {
48  tm t;
49  localtime_safe(&t, &time);
50 
51  set(t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, 0);
52 }
53 
54 date_time::date_time(const MYSQL_TIME& t) : time() {
55  set(t.year, t.month, t.day, t.hour, t.minute, t.second, t.second_part / 1000);
56 }
57 
58 date_time::date_time(const std::string& dt) : time() { set(dt); }
59 
60 int date_time::compare(const date_time& dt) const {
61  if (year() < dt.year()) return -1;
62 
63  if (year() > dt.year()) return 1;
64 
65  if (month() < dt.month()) return -1;
66 
67  if (month() > dt.month()) return 1;
68 
69  if (day() < dt.day()) return -1;
70 
71  if (day() > dt.day()) return 1;
72 
73  return time::compare(dt);
74 }
75 
77  set(dt.year(), dt.month(), dt.day(), dt.hour(), dt.minute(), dt.second(), dt.millisecond());
78  return *this;
79 }
80 
81 bool date_time::operator==(const date_time& dt) const { return compare(dt) == 0; }
82 
83 bool date_time::operator!=(const date_time& dt) const { return compare(dt) != 0; }
84 
85 bool date_time::operator<(const date_time& dt) const { return compare(dt) < 0; }
86 
87 bool date_time::operator<=(const date_time& dt) const { return compare(dt) <= 0; }
88 
89 bool date_time::operator>(const date_time& dt) const { return compare(dt) > 0; }
90 
91 bool date_time::operator>=(const date_time& dt) const { return compare(dt) >= 0; }
92 
94  m_year = year;
95  m_month = month;
96  m_day = day;
97  return true;
98 }
99 
101  m_year = year;
102  m_month = month;
103  m_day = day;
104 
105  return time::set(hour, minute, second, millisecond);
106 }
107 
108 u16 date_time::year() const { return m_year; }
109 
111  if (year == 0)
113 
114  m_year = year;
115 
116  if (!valid_date(year, month(), day())) {
117  m_month = 1;
118  m_day = 1;
119  }
120 
121  return m_year;
122 }
123 
124 u8 date_time::month() const { return m_month; }
125 
127  if (month == 0 || month > 12)
128  MARIADB_ERROR_THROW_DATE(year(), month, day(), hour(), minute(), second(), millisecond());
129 
130  m_month = month;
131 
132  u8 days_inmonth = days_in_month(year(), month);
133 
134  if (day() > days_inmonth) m_day = 1;
135 
136  return m_month;
137 }
138 
139 u8 date_time::day() const { return m_day; }
140 
142  u8 days_inmonth = days_in_month(year(), month());
143 
144  if (day == 0 || day > days_inmonth)
146 
147  m_day = day;
148 
149  return m_day;
150 }
151 
152 u16 date_time::day_of_year() const { return day_of_year(year(), month(), day()); }
153 
155  date_time new_date = reverse_day_of_year(year(), day_of_year);
156 
157  month(new_date.month());
158  day(new_date.day());
159 
160  return day_of_year;
161 }
162 
164  date_time tmp = *this;
165 
166  if (!years) return tmp;
167 
168  tmp.year(m_year + years);
169  return tmp;
170 }
171 
173  date_time tmp = *this;
174 
175  if (!months) return tmp;
176 
177  s32 years = months / 12;
178 
179  months = (months % 12) + month();
180 
181  if (months >= 12) {
182  ++years;
183  months %= 12;
184  } else if (months < 0) {
185  --years;
186  months = 12 - months;
187  }
188 
189  if (years) tmp = tmp.add_years(years);
190 
191  tmp.month(months);
192  return tmp;
193 }
194 
196  date_time tmp = *this;
197 
198  if (!days) return tmp;
199 
200  s32 dir = days > 0 ? 1 : -1;
201  u16 y = year();
202  s32 days_in = days_in_year(y);
203  s32 day_of = day_of_year() + days;
204 
205  while (day_of > days_in || day_of < 0) {
206  day_of -= days_in * dir;
207  y += dir;
208  days_in = days_in_year(y);
209  }
210 
211  tmp.year(y);
212  tmp.day_of_year(day_of);
213  return tmp;
214 }
215 
217  date_time tmp = *this;
218 
219  if (!hours) return tmp;
220 
221  s32 days = hours / 24;
222 
223  hours = (hours % 24) + hour();
224 
225  if (hours >= 24) {
226  ++days;
227  hours %= 24;
228  } else if (hours < 0) {
229  --days;
230  hours = 24 - hours;
231  }
232 
233  if (days) tmp = tmp.add_days(days);
234 
235  tmp.hour(hours);
236  return tmp;
237 }
238 
240  date_time tmp = *this;
241 
242  if (!minutes) return tmp;
243 
244  s32 hours = minutes / 60;
245 
246  minutes = (minutes % 60) + minute();
247 
248  if (minutes >= 60) {
249  ++hours;
250  minutes %= 60;
251  } else if (minutes < 0) {
252  --hours;
253  minutes = 60 - minutes;
254  }
255 
256  if (hours > 0) tmp = tmp.add_hours(hours);
257 
258  tmp.minute(minutes);
259  return tmp;
260 }
261 
263  date_time tmp = *this;
264 
265  if (!seconds) return tmp;
266 
267  s32 minutes = seconds / 60;
268 
269  seconds = (seconds % 60) + second();
270 
271  if (seconds >= 60) {
272  ++minutes;
273  seconds %= 60;
274  } else if (seconds < 0) {
275  --minutes;
276  seconds = 60 - seconds;
277  }
278 
279  if (minutes > 0) tmp = tmp.add_minutes(minutes);
280 
281  tmp.second(seconds);
282  return tmp;
283 }
284 
286  date_time tmp = *this;
287 
288  if (!milliseconds) return tmp;
289 
290  s32 seconds = milliseconds / 1000;
291 
292  milliseconds = (milliseconds % 1000) + millisecond();
293 
294  if (milliseconds >= 1000) {
295  ++seconds;
296  milliseconds %= 1000;
297  } else if (milliseconds < 0) {
298  --seconds;
299  milliseconds = 1000 - milliseconds;
300  }
301 
302  if (seconds > 0) tmp = tmp.add_seconds(seconds);
303 
304  tmp.millisecond(milliseconds);
305  return tmp;
306 }
307 
309  bool negative = !dur.negative();
310 
311  time_span tmp = dur;
312  tmp.negative(negative);
313  return add(tmp);
314 }
315 
316 date_time date_time::add(const time_span& dur) const {
317  s32 negate = dur.negative() ? -1 : 1;
318  date_time tmp = *this;
319 
320  tmp = tmp.add_days(negate * dur.days());
321  tmp = tmp.add_hours(negate * dur.hours());
322  tmp = tmp.add_minutes(negate * dur.minutes());
323  tmp = tmp.add_seconds(negate * dur.seconds());
324  tmp = tmp.add_milliseconds(negate * dur.milliseconds());
325  return tmp;
326 }
327 
329  date_time tmp = *this;
330 
331  tmp = tmp.add_hours(t.hour());
332  tmp = tmp.add_minutes(t.minute());
333  tmp = tmp.add_seconds(t.second());
334  tmp = tmp.add_milliseconds(t.millisecond());
335  return tmp;
336 }
337 
339  date_time tmp = *this;
340 
341  tmp = tmp.add_hours(-1 * t.hour());
342  tmp = tmp.add_minutes(-1 * t.minute());
343  tmp = tmp.add_seconds(-1 * t.second());
344  tmp = tmp.add_milliseconds(-1 * t.millisecond());
345  return tmp;
346 }
347 
349  if (dt == *this) return time_span();
350 
351  if (dt > *this) {
352  time_span dur = dt.time_between(*this);
353  dur.negative(true);
354  return dur;
355  }
356 
357  s64 ms =
358  (hour() * MS_PER_HOUR) + (minute() * MS_PER_MIN) + (second() * MS_PER_SEC) + millisecond();
359  s64 dt_ms = (dt.hour() * MS_PER_HOUR) + (dt.minute() * MS_PER_MIN) +
360  (dt.second() * MS_PER_SEC) + dt.millisecond();
361  s64 total_ms = 0;
362  date_time stop_date_time;
363 
364  //
365  // Need to stop one day after in this case
366  //
367  if (dt_ms > ms) {
368  stop_date_time = dt.add_days(1);
369  total_ms = MS_PER_DAY - (dt_ms - ms);
370  } else {
371  stop_date_time = dt;
372  total_ms = ms - dt_ms;
373  }
374 
375  u32 hours = static_cast<u32>(total_ms / MS_PER_HOUR);
376  total_ms = total_ms % MS_PER_HOUR;
377 
378  u32 minutes = static_cast<u32>(total_ms / MS_PER_MIN);
379  total_ms = total_ms % MS_PER_MIN;
380 
381  u32 seconds = static_cast<u32>(total_ms / MS_PER_SEC);
382  total_ms = total_ms % MS_PER_SEC;
383 
384  u32 milliseconds = static_cast<u32>(total_ms);
385 
386  u32 totaldays = 0;
387  u16 currentyear = year();
388 
389  while (currentyear > stop_date_time.year()) {
390  if (currentyear == year())
391  totaldays += day_of_year();
392  else
393  totaldays += days_in_year(currentyear);
394 
395  currentyear--;
396  }
397 
398  if (year() == stop_date_time.year())
399  totaldays += day_of_year() - stop_date_time.day_of_year();
400  else
401  totaldays += days_in_year(currentyear) - stop_date_time.day_of_year();
402 
403  /*while (currentday > stop_date_time.day_of_year())
404  {
405  totaldays++;
406  currentday--;
407  }*/
408 
409  return time_span(totaldays, hours, minutes, seconds, milliseconds, false);
410 }
411 
412 bool date_time::is_valid() const {
413  return valid_date(year(), month(), day()) && time::is_valid();
414 }
415 
417  /*
418  * Leap year rule:
419  * every year divisible by 400 is a leap year
420  * every year divisible by 4 is a leap year if its not divisible by 100
421  */
422  return (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
423 }
424 
426  if (year == 0 || month == 0 || day == 0) return false;
427 
428  if (month > 12) return false;
429 
430  return day <= days_in_month(year, month);
431 }
432 
433 u16 date_time::days_in_year(u16 year) { return is_leap_year(year) ? u16(366) : u16(365); }
434 
436  u8 days_in_month = g_month_lengths[month - 1];
437 
438  if (month == 2 && is_leap_year(year)) days_in_month++;
439 
440  return days_in_month;
441 }
442 
444  u16 day_of_year = 0;
445 
446  for (u8 month = 1; month < _month; month++)
447  day_of_year += date_time::days_in_month(year, month);
448 
449  day_of_year += day;
450 
451  return day_of_year;
452 }
453 
455  u8 month;
456 
457  for (month = 1; month < 12; month++) {
458  u32 days = date_time::days_in_month(year, month);
459 
460  if (days >= day_of_year) break;
461 
462  day_of_year -= days;
463  }
464 
465  return date_time(year, month, static_cast<u8>(day_of_year));
466 }
467 
468 time_t date_time::mktime() const {
469  tm t;
470 
471  t.tm_year = year() - 1900;
472  t.tm_mon = month() - 1;
473  t.tm_mday = day();
474  t.tm_hour = hour();
475  t.tm_min = minute();
476  t.tm_sec = second();
477 
478  return ::mktime(&t);
479 }
480 
481 MYSQL_TIME date_time::mysql_time() const {
482  MYSQL_TIME t;
483 
484  t.year = year();
485  t.month = month();
486  t.day = day();
487  t.hour = hour();
488  t.minute = minute();
489  t.second = second();
490  t.second_part = millisecond() * 1000u;
491  t.neg = false;
492  t.time_type =
493  !t.hour && !t.minute && !t.second ? MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME;
494 
495  return t;
496 }
497 
498 double date_time::diff_time(const date_time& dt) const {
499  time_t time_val = mktime();
500  time_t dt_time_val = dt.mktime();
501 
502  return ::difftime(time_val, dt_time_val);
503 }
504 
505 date_time date_time::date() const { return date_time(year(), month(), day()); }
506 
508  using namespace std::chrono;
509  auto now = system_clock::now();
510 
511  time_t local_time = system_clock::to_time_t(now);
512  tm ts;
513  localtime_safe(&ts, &local_time);
514 
515  auto millis = duration_cast<milliseconds>(now.time_since_epoch()).count() % 1000;
516  return date_time(ts).add_milliseconds(static_cast<s32>(millis));
517 }
518 
520  using namespace std::chrono;
521  auto now = system_clock::now();
522 
523  time_t utc_time = system_clock::to_time_t(now);
524  tm ts;
525  gmtime_safe(&ts, &utc_time);
526 
527  auto millis = duration_cast<milliseconds>(now.time_since_epoch()).count() % 1000;
528  return date_time(ts).add_milliseconds(static_cast<s32>(millis));
529 }
530 
531 bool date_time::set(const std::string& dt) {
532  std::stringstream stream(dt);
533 
534  u8 m, d;
535  u16 s_y = 0, s_m = 0, s_d = 0;
536  char delim;
537 
538  // read formatted years, check overflow before cast
539  if (stream >> s_y) {
540  if (stream.eof()) return set(s_y, 0, 0);
541 
542  // read formatted months, check overflow before cast
543  if (stream >> delim && stream >> s_m && s_m < 13) {
544  m = static_cast<u8>(s_m);
545  if (stream.eof()) return set(s_y, m, 0);
546 
547  // read formatted days, check overflow before cast
548  if (stream >> delim && stream >> s_d && s_d < 32) {
549  d = static_cast<u8>(s_d);
550  if (stream.eof()) return set(s_y, m, d);
551 
552  // extract remaining string
553  std::string rest;
554  std::getline(stream, rest);
555 
556  // set time from string
557  return set(s_y, m, d) && time::set(rest);
558  }
559  }
560  }
561 
562  throw std::invalid_argument("invalid date format");
563 }
564 
565 const std::string date_time::str(bool with_millisecond) const {
566  std::stringstream stream;
567  stream.fill('0');
568 
569  stream << str_date() << " " << time::str_time(with_millisecond);
570  return stream.str();
571 }
572 
574  std::stringstream stream;
575  stream.fill('0');
576 
577  /*
578  * Note: the magic unary + forces conversion of unsigned char (u8) to a numeric printing type,
579  * otherwise it will
580  * be printed as char
581  */
582  stream << std::setw(4) << year() << "-" << std::setw(2) << +month() << "-" << std::setw(2)
583  << +day();
584  return stream.str();
585 }
586 
587 std::ostream& mariadb::operator<<(std::ostream& os, const date_time& dt) {
588  os << dt.str(true);
589  return os;
590 }
u16 millisecond() const
Get the current millisecond 0-999.
Definition: time.cpp:121
bool is_valid() const override
Indicates whether this date is considered valid.
Definition: date_time.cpp:412
signed int s32
Definition: types.hpp:25
u16 day_of_year() const
Calculates the day of year from current date.
Definition: date_time.cpp:152
Class representing SQL time.
Definition: time.hpp:23
bool operator>=(const date_time &dt) const
Definition: date_time.cpp:91
#define MS_PER_MIN
Definition: date_time.cpp:27
date_time add_minutes(s32 minutes) const
Add minutes to current date with hour wrapping.
Definition: date_time.cpp:239
static u16 days_in_year(u16 year)
Gets the number of days in a given year.
Definition: date_time.cpp:433
virtual bool is_valid() const
Indicates whether this time is considered valid.
Definition: time.cpp:293
date_time add_years(s32 years) const
Add years to current date.
Definition: date_time.cpp:163
u8 second() const
Get the current second 0-61 (leap second possible)
Definition: time.cpp:110
date_time subtract(const time_span &dur) const
Subtracts a timespan from the date_time.
Definition: date_time.cpp:308
date_time substract(const time &t) const
Substract a mariadb::time from the date_time.
Definition: date_time.cpp:338
u8 minute() const
Get the current minute 0-59.
Definition: time.cpp:99
static date_time now()
Gets the current date and time as date_time.
Definition: date_time.cpp:507
u8 seconds() const
Get number of seconds.
Definition: time_span.cpp:102
int compare(const time &t) const
Compare this instance to given instance.
Definition: time.cpp:45
#define d
#define MS_PER_SEC
Definition: date_time.cpp:26
date_time add_hours(s32 hours) const
Add hours to current date with day wrapping.
Definition: date_time.cpp:216
static bool is_leap_year(u16 year)
Indicates whether a given year is leap according to gregorian calendar.
Definition: date_time.cpp:416
std::ostream & operator<<(std::ostream &os, const date_time &ddt)
Definition: date_time.cpp:587
MYSQL_TIME mysql_time() const
Converts the date_time to a MYSQL_TIME.
Definition: date_time.cpp:481
u32 days() const
Get number of days.
Definition: time_span.cpp:82
u16 milliseconds() const
Get number of milliseconds.
Definition: time_span.cpp:110
const std::string str_time(bool with_millisecond=false) const
Converts the time to a string with the format hh:mm:ss[.nnn].
Definition: time.cpp:357
u16 year() const
Get currently set year.
Definition: date_time.cpp:108
u8 hour() const
Get the current hour 0-23.
Definition: time.cpp:88
date_time & operator=(const date_time &dt)
Definition: date_time.cpp:76
bool operator>(const date_time &dt) const
Definition: date_time.cpp:89
int compare(const date_time &dt) const
Definition: date_time.cpp:60
date_time add(const time_span &dur) const
Adds a timespan to the date_time.
Definition: date_time.cpp:316
double diff_time(const date_time &dt) const
Uses ::difftime to calculate number of seconds between two dates.
Definition: date_time.cpp:498
bool operator!=(const date_time &dt) const
Definition: date_time.cpp:83
unsigned char u8
Definition: types.hpp:20
static u8 days_in_month(u16 year, u8 month)
Gets the number of days in a given month of a given year.
Definition: date_time.cpp:435
const std::string str_date() const
Converts only the date part of this date_time to ISO 8601 date string yyyy-mm-dd. ...
Definition: date_time.cpp:573
static date_time reverse_day_of_year(u16 year, u16 day_of_year)
Gets the date from given year and day of year.
Definition: date_time.cpp:454
unsigned short u16
Definition: types.hpp:21
date_time add_seconds(s32 seconds) const
Add seconds to current date with minute wrapping.
Definition: date_time.cpp:262
date_time add_milliseconds(s32 milliseconds) const
Add milliseconds to current date with second wrapping.
Definition: date_time.cpp:285
date_time add_days(s32 days) const
Add days to current date with month wrapping.
Definition: date_time.cpp:195
signed long long s64
Definition: types.hpp:35
#define MS_PER_HOUR
Definition: date_time.cpp:28
bool operator<(const date_time &dt) const
Definition: date_time.cpp:85
int gmtime_safe(struct tm *_tm, const time_t *_time)
Definition: private.hpp:27
date_time add_months(s32 months) const
Add months to current date with year wrapping.
Definition: date_time.cpp:172
bool operator<=(const date_time &dt) const
Definition: date_time.cpp:87
Class used to represent SQL date_time.
Definition: date_time.hpp:20
bool set(u16 year, u8 month, u8 day)
Set only date part.
Definition: date_time.cpp:93
time_span time_between(const date_time &dt) const
Calculates the time_span between this date_time and dt.
Definition: date_time.cpp:348
#define MS_PER_DAY
Definition: date_time.cpp:29
time_t mktime() const
Convert the date_time to a time_t.
Definition: date_time.cpp:468
date_time date() const
Gets only the date part of this date_time.
Definition: date_time.cpp:505
bool operator==(const date_time &dt) const
Definition: date_time.cpp:81
double t
Definition: astarsearch.cpp:64
static bool valid_date(u16 year, u8 month, u8 day)
Indicates whether a given date is valid in terms of existing month and day in month.
Definition: date_time.cpp:425
bool negative() const
Indicates whether this time_span is negative.
Definition: time_span.cpp:118
u8 day() const
Get currently set day of month.
Definition: date_time.cpp:139
u8 minutes() const
Get number of hours.
Definition: time_span.cpp:94
#define MARIADB_ERROR_THROW_DATE(_year, _month, _day, _hour, _minute, _second, _millisecond)
Definition: private.hpp:46
virtual bool set(const std::string &t)
Set the time from string The format needs to be hh[:mm][:ss][.nnn] where less digits are possible and...
Definition: time.cpp:326
u8 month() const
Get currently set month.
Definition: date_time.cpp:124
unsigned int u32
Definition: types.hpp:22
u8 hours() const
Get number of hours.
Definition: time_span.cpp:86
int localtime_safe(struct tm *_tm, const time_t *_time)
Definition: private.hpp:23
date_time(u16 year=1970, u8 month=1, u8 day=1, u8 hour=0, u8 minute=0, u8 second=0, u16 millisecond=0)
Construct date_time from given data.
Definition: date_time.cpp:31
static date_time now_utc()
Gets the current date and time as date_time.
Definition: date_time.cpp:519
const std::string str(bool with_millisecond=false) const
Converts the date and time to ISO 8601 string yyyy-mm-dd hh:mm:ss[.nnn].
Definition: date_time.cpp:565