C++ Utilities  4.17.0
Useful C++ classes and routines such as argument parser, IO and conversion utilities
datetime.cpp
Go to the documentation of this file.
1 #include "./datetime.h"
2 
3 #include "../conversion/stringbuilder.h"
4 #include "../conversion/stringconversion.h"
5 
6 #include <iomanip>
7 #include <sstream>
8 #include <stdexcept>
9 
10 using namespace std;
11 using namespace ChronoUtilities;
12 using namespace ConversionUtilities;
13 
14 const int DateTime::m_daysPerYear = 365;
15 const int DateTime::m_daysPer4Years = 1461;
16 const int DateTime::m_daysPer100Years = 36524;
17 const int DateTime::m_daysPer400Years = 146097;
18 const int DateTime::m_daysTo1601 = 584388;
19 const int DateTime::m_daysTo1899 = 693593;
20 const int DateTime::m_daysTo10000 = 3652059;
21 const int DateTime::m_daysToMonth365[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
22 const int DateTime::m_daysToMonth366[13] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
23 const int DateTime::m_daysInMonth365[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
24 const int DateTime::m_daysInMonth366[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
25 
26 template <typename num1, typename num2, typename num3> constexpr bool inRangeInclMax(num1 val, num2 min, num3 max)
27 {
28  return (val) >= (min) && (val) <= (max);
29 }
30 
31 template <typename num1, typename num2, typename num3> constexpr bool inRangeExclMax(num1 val, num2 min, num3 max)
32 {
33  return (val) >= (min) && (val) < (max);
34 }
35 
59 DateTime DateTime::fromTimeStamp(time_t timeStamp)
60 {
61  if (timeStamp) {
62  struct tm *timeinfo = localtime(&timeStamp);
63  return DateTime::fromDateAndTime(timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min,
64  timeinfo->tm_sec < 60 ? timeinfo->tm_sec : 59, 0);
65  } else {
66  return DateTime();
67  }
68 }
69 
73 DateTime DateTime::fromTimeStampGmt(time_t timeStamp)
74 {
75  return DateTime(DateTime::unixEpochStart().totalTicks() + static_cast<uint64>(timeStamp) * TimeSpan::m_ticksPerSecond);
76 }
77 
87 DateTime DateTime::fromString(const char *str)
88 {
89  int values[6] = { 0 };
90  int *const dayIndex = values + 2;
91  int *const secondsIndex = values + 5;
92  int *valueIndex = values;
93  int *const valuesEnd = values + 7;
94  double miliSecondsFact = 100.0, miliSeconds = 0.0;
95  for (const char *strIndex = str;; ++strIndex) {
96  const char c = *strIndex;
97  if (c <= '9' && c >= '0') {
98  if (valueIndex > secondsIndex) {
99  miliSeconds += (c - '0') * miliSecondsFact;
100  miliSecondsFact /= 10;
101  } else {
102  *valueIndex *= 10;
103  *valueIndex += c - '0';
104  }
105  } else if ((c == '-' || c == ':' || c == '/') || (c == '.' && (valueIndex == secondsIndex)) || (c == ' ' && (valueIndex == dayIndex))) {
106  if (++valueIndex == valuesEnd) {
107  break; // just ignore further values for now
108  }
109  } else if (c == '\0') {
110  break;
111  } else {
112  throw ConversionException(argsToString("unexpected ", c));
113  }
114  }
115  return DateTime::fromDateAndTime(values[0], values[1], *dayIndex, values[3], values[4], *secondsIndex, miliSeconds);
116 }
117 
127 std::pair<DateTime, TimeSpan> DateTime::fromIsoString(const char *str)
128 {
129  int values[9] = { 0 };
130  int *const dayIndex = values + 2;
131  int *const hourIndex = values + 3;
132  int *const secondsIndex = values + 5;
133  int *const miliSecondsIndex = values + 6;
134  int *const deltaHourIndex = values + 7;
135  int *valueIndex = values;
136  bool deltaNegative = false;
137  double miliSecondsFact = 100.0, miliSeconds = 0.0;
138  for (const char *strIndex = str;; ++strIndex) {
139  const char c = *strIndex;
140  if (c <= '9' && c >= '0') {
141  if (valueIndex == miliSecondsIndex) {
142  miliSeconds += (c - '0') * miliSecondsFact;
143  miliSecondsFact /= 10;
144  } else {
145  *valueIndex *= 10;
146  *valueIndex += c - '0';
147  }
148  } else if (c == 'T') {
149  if (++valueIndex != hourIndex) {
150  throw ConversionException("\"T\" expected before hour");
151  }
152  } else if (c == '-') {
153  if (valueIndex < dayIndex) {
154  ++valueIndex;
155  } else if (++valueIndex == deltaHourIndex) {
156  deltaNegative = true;
157  } else {
158  throw ConversionException("unexpected \"-\" after day");
159  }
160  } else if (c == '.') {
161  if (valueIndex != secondsIndex) {
162  throw ConversionException("unexpected \".\"");
163  } else {
164  ++valueIndex;
165  }
166  } else if (c == ':') {
167  if (valueIndex < hourIndex) {
168  throw ConversionException("unexpected \":\" before hour");
169  } else if (valueIndex == secondsIndex) {
170  throw ConversionException("unexpected \":\" after second");
171  } else {
172  ++valueIndex;
173  }
174  } else if ((c == '+') && (++valueIndex >= secondsIndex)) {
175  valueIndex = deltaHourIndex;
176  deltaNegative = false;
177  } else if ((c == 'Z') && (++valueIndex >= secondsIndex)) {
178  valueIndex = deltaHourIndex + 2;
179  } else if (c == '\0') {
180  break;
181  } else {
182  throw ConversionException(argsToString("unexpected \"", c, '\"'));
183  }
184  }
185  auto delta(TimeSpan::fromMinutes(*deltaHourIndex * 60 + values[8]));
186  if (deltaNegative) {
187  delta = TimeSpan(-delta.totalTicks());
188  }
189  return make_pair(DateTime::fromDateAndTime(values[0], values[1], *dayIndex, *hourIndex, values[4], *secondsIndex, miliSeconds), delta);
190 }
191 
197 string DateTime::toString(DateTimeOutputFormat format, bool noMilliseconds) const
198 {
199  string result;
200  toString(result, format, noMilliseconds);
201  return result;
202 }
203 
209 void DateTime::toString(string &result, DateTimeOutputFormat format, bool noMilliseconds) const
210 {
211  stringstream s(stringstream::in | stringstream::out);
212  s << setfill('0');
213  if (format == DateTimeOutputFormat::DateTimeAndWeekday || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
214  s << printDayOfWeek(dayOfWeek(), format == DateTimeOutputFormat::DateTimeAndShortWeekday) << ' ';
215  if (format == DateTimeOutputFormat::DateOnly || format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday
216  || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
217  s << setw(4) << year() << '-' << setw(2) << month() << '-' << setw(2) << day();
218  if (format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday
219  || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
220  s << " ";
221  if (format == DateTimeOutputFormat::TimeOnly || format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday
222  || format == DateTimeOutputFormat::DateTimeAndShortWeekday) {
223  s << setw(2) << hour() << ':' << setw(2) << minute() << ':' << setw(2) << second();
224  int ms = millisecond();
225  if (!noMilliseconds && ms > 0) {
226  s << '.' << setw(3) << ms;
227  }
228  }
229  result = s.str();
230 }
231 
236 string DateTime::toIsoString(TimeSpan timeZoneDelta) const
237 {
238  stringstream s(stringstream::in | stringstream::out);
239  s << setfill('0');
240  s << setw(4) << year() << '-' << setw(2) << month() << '-' << setw(2) << day() << 'T' << setw(2) << hour() << ':' << setw(2) << minute() << ':'
241  << setw(2) << second();
242  const int milli(millisecond());
243  const int micro(microsecond());
244  const int nano(nanosecond());
245  if (milli || micro || nano) {
246  s << '.' << setw(3) << milli;
247  if (micro || nano) {
248  s << setw(3) << micro;
249  if (nano) {
250  s << nano / TimeSpan::nanosecondsPerTick;
251  }
252  }
253  }
254  if (!timeZoneDelta.isNull()) {
255  if (timeZoneDelta.isNegative()) {
256  s << '-';
257  timeZoneDelta = TimeSpan(-timeZoneDelta.totalTicks());
258  } else {
259  s << '+';
260  }
261  s << setw(2) << timeZoneDelta.hours() << ':' << setw(2) << timeZoneDelta.minutes();
262  }
263  return s.str();
264 }
265 
273 const char *DateTime::printDayOfWeek(DayOfWeek dayOfWeek, bool abbreviation)
274 {
275  if (abbreviation) {
276  switch (dayOfWeek) {
277  case DayOfWeek::Monday:
278  return "Mon";
279  case DayOfWeek::Tuesday:
280  return "Tue";
281  case DayOfWeek::Wednesday:
282  return "Wed";
283  case DayOfWeek::Thursday:
284  return "Thu";
285  case DayOfWeek::Friday:
286  return "Fri";
287  case DayOfWeek::Saturday:
288  return "Sat";
289  case DayOfWeek::Sunday:
290  return "Sun";
291  }
292  } else {
293  switch (dayOfWeek) {
294  case DayOfWeek::Monday:
295  return "Monday";
296  case DayOfWeek::Tuesday:
297  return "Tuesday";
298  case DayOfWeek::Wednesday:
299  return "Wednesday";
300  case DayOfWeek::Thursday:
301  return "Thursday";
302  case DayOfWeek::Friday:
303  return "Friday";
304  case DayOfWeek::Saturday:
305  return "Saturday";
306  case DayOfWeek::Sunday:
307  return "Sunday";
308  }
309  }
310  return "";
311 }
312 
313 #if defined(PLATFORM_UNIX) && !defined(PLATFORM_MAC)
314 
318 DateTime DateTime::exactGmtNow()
319 {
320  struct timespec t;
321  clock_gettime(CLOCK_REALTIME, &t);
322  return DateTime(
323  DateTime::unixEpochStart().totalTicks() + static_cast<uint64>(t.tv_sec) * TimeSpan::m_ticksPerSecond + static_cast<uint64>(t.tv_nsec) / 100);
324 }
325 #endif
326 
330 uint64 DateTime::dateToTicks(int year, int month, int day)
331 {
332  if (!inRangeInclMax(year, 1, 9999)) {
333  throw ConversionException("year is out of range");
334  }
335  if (!inRangeInclMax(month, 1, 12)) {
336  throw ConversionException("month is out of range");
337  }
338  const auto *const daysToMonth = reinterpret_cast<const int *>(isLeapYear(year) ? m_daysToMonth366 : m_daysToMonth365);
339  const int passedMonth = month - 1;
340  if (!inRangeInclMax(day, 1, daysToMonth[month] - daysToMonth[passedMonth])) {
341  throw ConversionException("day is out of range");
342  }
343  const auto passedYears = static_cast<unsigned int>(year - 1);
344  const auto passedDays = static_cast<unsigned int>(day - 1);
345  return (passedYears * m_daysPerYear + passedYears / 4 - passedYears / 100 + passedYears / 400
346  + static_cast<unsigned int>(daysToMonth[passedMonth]) + passedDays)
347  * TimeSpan::m_ticksPerDay;
348 }
349 
353 uint64 DateTime::timeToTicks(int hour, int minute, int second, double millisecond)
354 {
355  if (!inRangeExclMax(hour, 0, 24)) {
356  throw ConversionException("hour is out of range");
357  }
358  if (!inRangeExclMax(minute, 0, 60)) {
359  throw ConversionException("minute is out of range");
360  }
361  if (!inRangeExclMax(second, 0, 60)) {
362  throw ConversionException("second is out of range");
363  }
364  if (!inRangeExclMax(millisecond, 0.0, 1000.0)) {
365  throw ConversionException("millisecond is out of range");
366  }
367  return static_cast<uint64>(hour * TimeSpan::m_ticksPerHour) + static_cast<uint64>(minute * TimeSpan::m_ticksPerMinute)
368  + static_cast<uint64>(second * TimeSpan::m_ticksPerSecond) + static_cast<uint64>(millisecond * TimeSpan::m_ticksPerMillisecond);
369 }
370 
375 int DateTime::getDatePart(DatePart part) const
376 {
377  const int fullDays = m_ticks / TimeSpan::m_ticksPerDay;
378  const int full400YearBlocks = fullDays / m_daysPer400Years;
379  const int daysMinusFull400YearBlocks = fullDays - full400YearBlocks * m_daysPer400Years;
380  int full100YearBlocks = daysMinusFull400YearBlocks / m_daysPer100Years;
381  if (full100YearBlocks == 4) {
382  full100YearBlocks = 3;
383  }
384  const int daysMinusFull100YearBlocks = daysMinusFull400YearBlocks - full100YearBlocks * m_daysPer100Years;
385  const int full4YearBlocks = daysMinusFull100YearBlocks / m_daysPer4Years;
386  const int daysMinusFull4YearBlocks = daysMinusFull100YearBlocks - full4YearBlocks * m_daysPer4Years;
387  int full1YearBlocks = daysMinusFull4YearBlocks / m_daysPerYear;
388  if (full1YearBlocks == 4) {
389  full1YearBlocks = 3;
390  }
391  if (part == DatePart::Year) {
392  return full400YearBlocks * 400 + full100YearBlocks * 100 + full4YearBlocks * 4 + full1YearBlocks + 1;
393  }
394  const int restDays = daysMinusFull4YearBlocks - full1YearBlocks * m_daysPerYear;
395  if (part == DatePart::DayOfYear) { // day
396  return restDays + 1;
397  }
398  const int *const daysToMonth = (full1YearBlocks == 3 && (full4YearBlocks != 24 || full100YearBlocks == 3)) ? m_daysToMonth366 : m_daysToMonth365;
399  int month = 1;
400  while (restDays >= daysToMonth[month]) {
401  ++month;
402  }
403  if (part == DatePart::Month) {
404  return month;
405  } else if (part == DatePart::Day) {
406  return restDays - daysToMonth[month - 1] + 1;
407  }
408  return 0;
409 }
constexpr bool isNegative() const
Returns ture if the time interval represented by the current TimeSpan class is negative.
Definition: timespan.h:400
constexpr bool inRangeExclMax(num1 val, num2 min, num3 max)
Definition: datetime.cpp:31
Represents an instant in time, typically expressed as a date and time of day.
Definition: datetime.h:52
Contains classes providing a means for handling date and time information.
Definition: datetime.h:12
constexpr int64 totalTicks() const
Returns the number of ticks that represent the value of the current TimeSpan class.
Definition: timespan.h:196
The ConversionException class is thrown by the various conversion functions of this library when a co...
Represents a time interval.
Definition: timespan.h:28
constexpr int hours() const
Returns the hours component of the time interval represented by the current TimeSpan class.
Definition: timespan.h:294
constexpr StringType argsToString(Args &&... args)
std::uint64_t uint64
unsigned 64-bit integer
Definition: types.h:49
constexpr bool isNull() const
Returns ture if the time interval represented by the current TimeSpan class is null.
Definition: timespan.h:392
constexpr T max(T first, T second)
Returns the greatest of the given items.
Definition: math.h:29
constexpr int minutes() const
Returns the minutes component of the time interval represented by the current TimeSpan class.
Definition: timespan.h:286
DatePart
Specifies the date part.
Definition: datetime.h:45
Contains several functions providing conversions between different data types.
DateTimeOutputFormat
Specifies the output format.
Definition: datetime.h:18
constexpr bool inRangeInclMax(num1 val, num2 min, num3 max)
Definition: datetime.cpp:26
constexpr T min(T first, T second)
Returns the smallest of the given items.
Definition: math.h:17
DayOfWeek
Specifies the day of the week.
Definition: datetime.h:30