C++ Utilities  5.0.1
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 
12 namespace CppUtilities {
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 
58 DateTime DateTime::fromTimeStamp(time_t timeStamp)
59 {
60  if (timeStamp) {
61  struct tm *const timeinfo = localtime(&timeStamp);
62  return DateTime::fromDateAndTime(timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min,
63  timeinfo->tm_sec < 60 ? timeinfo->tm_sec : 59, 0);
64  } else {
65  return DateTime();
66  }
67 }
68 
78 DateTime DateTime::fromString(const char *str)
79 {
80  int values[6] = { 0 };
81  int *const dayIndex = values + 2;
82  int *const secondsIndex = values + 5;
83  int *valueIndex = values;
84  int *const valuesEnd = values + 7;
85  double miliSecondsFact = 100.0, miliSeconds = 0.0;
86  for (const char *strIndex = str;; ++strIndex) {
87  const char c = *strIndex;
88  if (c <= '9' && c >= '0') {
89  if (valueIndex > secondsIndex) {
90  miliSeconds += (c - '0') * miliSecondsFact;
91  miliSecondsFact /= 10;
92  } else {
93  *valueIndex *= 10;
94  *valueIndex += c - '0';
95  }
96  } else if ((c == '-' || c == ':' || c == '/') || (c == '.' && (valueIndex == secondsIndex)) || (c == ' ' && (valueIndex == dayIndex))) {
97  if (++valueIndex == valuesEnd) {
98  break; // just ignore further values for now
99  }
100  } else if (c == '\0') {
101  break;
102  } else {
103  throw ConversionException(argsToString("unexpected ", c));
104  }
105  }
106  return DateTime::fromDateAndTime(values[0], values[1], *dayIndex, values[3], values[4], *secondsIndex, miliSeconds);
107 }
108 
118 std::pair<DateTime, TimeSpan> DateTime::fromIsoString(const char *str)
119 {
120  int values[9] = { 0 };
121  int *const dayIndex = values + 2;
122  int *const hourIndex = values + 3;
123  int *const secondsIndex = values + 5;
124  int *const miliSecondsIndex = values + 6;
125  int *const deltaHourIndex = values + 7;
126  int *valueIndex = values;
127  bool deltaNegative = false;
128  double miliSecondsFact = 100.0, miliSeconds = 0.0;
129  for (const char *strIndex = str;; ++strIndex) {
130  const char c = *strIndex;
131  if (c <= '9' && c >= '0') {
132  if (valueIndex == miliSecondsIndex) {
133  miliSeconds += (c - '0') * miliSecondsFact;
134  miliSecondsFact /= 10;
135  } else {
136  *valueIndex *= 10;
137  *valueIndex += c - '0';
138  }
139  } else if (c == 'T') {
140  if (++valueIndex != hourIndex) {
141  throw ConversionException("\"T\" expected before hour");
142  }
143  } else if (c == '-') {
144  if (valueIndex < dayIndex) {
145  ++valueIndex;
146  } else if (++valueIndex == deltaHourIndex) {
147  deltaNegative = true;
148  } else {
149  throw ConversionException("unexpected \"-\" after day");
150  }
151  } else if (c == '.') {
152  if (valueIndex != secondsIndex) {
153  throw ConversionException("unexpected \".\"");
154  } else {
155  ++valueIndex;
156  }
157  } else if (c == ':') {
158  if (valueIndex < hourIndex) {
159  throw ConversionException("unexpected \":\" before hour");
160  } else if (valueIndex == secondsIndex) {
161  throw ConversionException("unexpected \":\" after second");
162  } else {
163  ++valueIndex;
164  }
165  } else if ((c == '+') && (++valueIndex >= secondsIndex)) {
166  valueIndex = deltaHourIndex;
167  deltaNegative = false;
168  } else if ((c == 'Z') && (++valueIndex >= secondsIndex)) {
169  valueIndex = deltaHourIndex + 2;
170  } else if (c == '\0') {
171  break;
172  } else {
173  throw ConversionException(argsToString("unexpected \"", c, '\"'));
174  }
175  }
176  auto delta(TimeSpan::fromMinutes(*deltaHourIndex * 60 + values[8]));
177  if (deltaNegative) {
178  delta = TimeSpan(-delta.totalTicks());
179  }
180  return make_pair(DateTime::fromDateAndTime(values[0], values[1], *dayIndex, *hourIndex, values[4], *secondsIndex, miliSeconds), delta);
181 }
182 
188 void DateTime::toString(string &result, DateTimeOutputFormat format, bool noMilliseconds) const
189 {
190  stringstream s(stringstream::in | stringstream::out);
191  s << setfill('0');
192  if (format == DateTimeOutputFormat::DateTimeAndWeekday || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
193  s << printDayOfWeek(dayOfWeek(), format == DateTimeOutputFormat::DateTimeAndShortWeekday) << ' ';
194  if (format == DateTimeOutputFormat::DateOnly || format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday
195  || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
196  s << setw(4) << year() << '-' << setw(2) << month() << '-' << setw(2) << day();
197  if (format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday
198  || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
199  s << " ";
200  if (format == DateTimeOutputFormat::TimeOnly || format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday
201  || format == DateTimeOutputFormat::DateTimeAndShortWeekday) {
202  s << setw(2) << hour() << ':' << setw(2) << minute() << ':' << setw(2) << second();
203  int ms = millisecond();
204  if (!noMilliseconds && ms > 0) {
205  s << '.' << setw(3) << ms;
206  }
207  }
208  result = s.str();
209 }
210 
215 string DateTime::toIsoString(TimeSpan timeZoneDelta) const
216 {
217  stringstream s(stringstream::in | stringstream::out);
218  s << setfill('0');
219  s << setw(4) << year() << '-' << setw(2) << month() << '-' << setw(2) << day() << 'T' << setw(2) << hour() << ':' << setw(2) << minute() << ':'
220  << setw(2) << second();
221  const int milli(millisecond());
222  const int micro(microsecond());
223  const int nano(nanosecond());
224  if (milli || micro || nano) {
225  s << '.' << setw(3) << milli;
226  if (micro || nano) {
227  s << setw(3) << micro;
228  if (nano) {
229  s << nano / TimeSpan::nanosecondsPerTick;
230  }
231  }
232  }
233  if (!timeZoneDelta.isNull()) {
234  if (timeZoneDelta.isNegative()) {
235  s << '-';
236  timeZoneDelta = TimeSpan(-timeZoneDelta.totalTicks());
237  } else {
238  s << '+';
239  }
240  s << setw(2) << timeZoneDelta.hours() << ':' << setw(2) << timeZoneDelta.minutes();
241  }
242  return s.str();
243 }
244 
252 const char *DateTime::printDayOfWeek(DayOfWeek dayOfWeek, bool abbreviation)
253 {
254  if (abbreviation) {
255  switch (dayOfWeek) {
256  case DayOfWeek::Monday:
257  return "Mon";
258  case DayOfWeek::Tuesday:
259  return "Tue";
260  case DayOfWeek::Wednesday:
261  return "Wed";
262  case DayOfWeek::Thursday:
263  return "Thu";
264  case DayOfWeek::Friday:
265  return "Fri";
266  case DayOfWeek::Saturday:
267  return "Sat";
268  case DayOfWeek::Sunday:
269  return "Sun";
270  }
271  } else {
272  switch (dayOfWeek) {
273  case DayOfWeek::Monday:
274  return "Monday";
275  case DayOfWeek::Tuesday:
276  return "Tuesday";
277  case DayOfWeek::Wednesday:
278  return "Wednesday";
279  case DayOfWeek::Thursday:
280  return "Thursday";
281  case DayOfWeek::Friday:
282  return "Friday";
283  case DayOfWeek::Saturday:
284  return "Saturday";
285  case DayOfWeek::Sunday:
286  return "Sunday";
287  }
288  }
289  return "";
290 }
291 
292 #if defined(PLATFORM_UNIX) && !defined(PLATFORM_MAC)
293 
297 DateTime DateTime::exactGmtNow()
298 {
299  struct timespec t;
300  clock_gettime(CLOCK_REALTIME, &t);
301  return DateTime(DateTime::unixEpochStart().totalTicks() + static_cast<std::uint64_t>(t.tv_sec) * TimeSpan::ticksPerSecond
302  + static_cast<std::uint64_t>(t.tv_nsec) / 100);
303 }
304 #endif
305 
309 std::uint64_t DateTime::dateToTicks(int year, int month, int day)
310 {
311  if (!inRangeInclMax(year, 1, 9999)) {
312  throw ConversionException("year is out of range");
313  }
314  if (!inRangeInclMax(month, 1, 12)) {
315  throw ConversionException("month is out of range");
316  }
317  const auto *const daysToMonth = reinterpret_cast<const int *>(isLeapYear(year) ? m_daysToMonth366 : m_daysToMonth365);
318  const int passedMonth = month - 1;
319  if (!inRangeInclMax(day, 1, daysToMonth[month] - daysToMonth[passedMonth])) {
320  throw ConversionException("day is out of range");
321  }
322  const auto passedYears = static_cast<unsigned int>(year - 1);
323  const auto passedDays = static_cast<unsigned int>(day - 1);
324  return (passedYears * m_daysPerYear + passedYears / 4 - passedYears / 100 + passedYears / 400
325  + static_cast<unsigned int>(daysToMonth[passedMonth]) + passedDays)
326  * TimeSpan::ticksPerDay;
327 }
328 
332 std::uint64_t DateTime::timeToTicks(int hour, int minute, int second, double millisecond)
333 {
334  if (!inRangeExclMax(hour, 0, 24)) {
335  throw ConversionException("hour is out of range");
336  }
337  if (!inRangeExclMax(minute, 0, 60)) {
338  throw ConversionException("minute is out of range");
339  }
340  if (!inRangeExclMax(second, 0, 60)) {
341  throw ConversionException("second is out of range");
342  }
343  if (!inRangeExclMax(millisecond, 0.0, 1000.0)) {
344  throw ConversionException("millisecond is out of range");
345  }
346  return static_cast<std::uint64_t>(hour * TimeSpan::ticksPerHour) + static_cast<std::uint64_t>(minute * TimeSpan::ticksPerMinute)
347  + static_cast<std::uint64_t>(second * TimeSpan::ticksPerSecond) + static_cast<std::uint64_t>(millisecond * TimeSpan::ticksPerMillisecond);
348 }
349 
354 int DateTime::getDatePart(DatePart part) const
355 {
356  const int fullDays = m_ticks / TimeSpan::ticksPerDay;
357  const int full400YearBlocks = fullDays / m_daysPer400Years;
358  const int daysMinusFull400YearBlocks = fullDays - full400YearBlocks * m_daysPer400Years;
359  int full100YearBlocks = daysMinusFull400YearBlocks / m_daysPer100Years;
360  if (full100YearBlocks == 4) {
361  full100YearBlocks = 3;
362  }
363  const int daysMinusFull100YearBlocks = daysMinusFull400YearBlocks - full100YearBlocks * m_daysPer100Years;
364  const int full4YearBlocks = daysMinusFull100YearBlocks / m_daysPer4Years;
365  const int daysMinusFull4YearBlocks = daysMinusFull100YearBlocks - full4YearBlocks * m_daysPer4Years;
366  int full1YearBlocks = daysMinusFull4YearBlocks / m_daysPerYear;
367  if (full1YearBlocks == 4) {
368  full1YearBlocks = 3;
369  }
370  if (part == DatePart::Year) {
371  return full400YearBlocks * 400 + full100YearBlocks * 100 + full4YearBlocks * 4 + full1YearBlocks + 1;
372  }
373  const int restDays = daysMinusFull4YearBlocks - full1YearBlocks * m_daysPerYear;
374  if (part == DatePart::DayOfYear) { // day
375  return restDays + 1;
376  }
377  const int *const daysToMonth = (full1YearBlocks == 3 && (full4YearBlocks != 24 || full100YearBlocks == 3)) ? m_daysToMonth366 : m_daysToMonth365;
378  int month = 1;
379  while (restDays >= daysToMonth[month]) {
380  ++month;
381  }
382  if (part == DatePart::Month) {
383  return month;
384  } else if (part == DatePart::Day) {
385  return restDays - daysToMonth[month - 1] + 1;
386  }
387  return 0;
388 }
389 
390 } // namespace CppUtilities
CppUtilities::DateTimeOutputFormat
DateTimeOutputFormat
Specifies the output format.
Definition: datetime.h:17
CppUtilities::inRangeExclMax
constexpr bool inRangeExclMax(num1 val, num2 min, num3 max)
Definition: datetime.cpp:31
CppUtilities::inRangeInclMax
constexpr bool inRangeInclMax(num1 val, num2 min, num3 max)
Definition: datetime.cpp:26
CppUtilities::max
constexpr T max(T first, T second)
Returns the greatest of the given items.
Definition: math.h:100
CppUtilities::argsToString
StringType argsToString(Args &&... args)
Definition: stringbuilder.h:147
CppUtilities::TimeSpan::totalTicks
constexpr std::int64_t totalTicks() const
Returns the number of ticks that represent the value of the current TimeSpan class.
Definition: timespan.h:185
CppUtilities::TimeSpan::hours
constexpr int hours() const
Returns the hours component of the time interval represented by the current TimeSpan class.
Definition: timespan.h:283
CppUtilities::DatePart
DatePart
Specifies the date part.
Definition: datetime.h:44
CppUtilities::TimeSpan::minutes
constexpr int minutes() const
Returns the minutes component of the time interval represented by the current TimeSpan class.
Definition: timespan.h:275
CppUtilities
Contains all utilities provides by the c++utilities library.
Definition: argumentparser.h:17
CppUtilities::ConversionException
The ConversionException class is thrown by the various conversion functions of this library when a co...
Definition: conversionexception.h:11
CppUtilities::DayOfWeek
DayOfWeek
Specifies the day of the week.
Definition: datetime.h:29
CppUtilities::min
constexpr T min(T first, T second)
Returns the smallest of the given items.
Definition: math.h:88
CppUtilities::TimeSpan
Represents a time interval.
Definition: timespan.h:25
CppUtilities::TimeSpan::isNegative
constexpr bool isNegative() const
Returns ture if the time interval represented by the current TimeSpan class is negative.
Definition: timespan.h:402
CppUtilities::DateTime
Represents an instant in time, typically expressed as a date and time of day.
Definition: datetime.h:51
CppUtilities::TimeSpan::isNull
constexpr bool isNull() const
Returns ture if the time interval represented by the current TimeSpan class is null.
Definition: timespan.h:394
datetime.h