C++ Utilities  4.9.2
Common C++ classes and routines used by my applications 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 #if defined(PLATFORM_UNIX)
11 #include <time.h>
12 #endif
13 
14 using namespace std;
15 using namespace ChronoUtilities;
16 using namespace ConversionUtilities;
17 
18 const int DateTime::m_daysPerYear = 365;
19 const int DateTime::m_daysPer4Years = 1461;
20 const int DateTime::m_daysPer100Years = 36524;
21 const int DateTime::m_daysPer400Years = 146097;
22 const int DateTime::m_daysTo1601 = 584388;
23 const int DateTime::m_daysTo1899 = 693593;
24 const int DateTime::m_daysTo10000 = 3652059;
25 const int DateTime::m_daysToMonth365[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
26 const int DateTime::m_daysToMonth366[13] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
27 const int DateTime::m_daysInMonth365[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
28 const int DateTime::m_daysInMonth366[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
29 
30 template <typename num1, typename num2, typename num3> inline bool inRangeInclMax(num1 val, num2 min, num3 max)
31 {
32  return (val) >= (min) && (val) <= (max);
33 }
34 
35 template <typename num1, typename num2, typename num3> inline bool inRangeExclMax(num1 val, num2 min, num3 max)
36 {
37  return (val) >= (min) && (val) < (max);
38 }
39 
56 DateTime DateTime::fromTimeStamp(time_t timeStamp)
57 {
58  if (timeStamp) {
59  struct tm *timeinfo = localtime(&timeStamp);
60  return DateTime::fromDateAndTime(timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min,
61  timeinfo->tm_sec < 60 ? timeinfo->tm_sec : 59, 0);
62  } else {
63  return DateTime();
64  }
65 }
66 
70 DateTime DateTime::fromTimeStampGmt(time_t timeStamp)
71 {
72  return DateTime(DateTime::unixEpochStart().totalTicks() + static_cast<uint64>(timeStamp) * TimeSpan::m_ticksPerSecond);
73 }
74 
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 
116 std::pair<DateTime, TimeSpan> DateTime::fromIsoString(const char *str)
117 {
118  int values[9] = { 0 };
119  int *const dayIndex = values + 2;
120  int *const hourIndex = values + 3;
121  int *const secondsIndex = values + 5;
122  int *const miliSecondsIndex = values + 6;
123  int *const deltaHourIndex = values + 7;
124  int *valueIndex = values;
125  bool deltaNegative = false;
126  double miliSecondsFact = 100.0, miliSeconds = 0.0;
127  for (const char *strIndex = str;; ++strIndex) {
128  const char c = *strIndex;
129  if (c <= '9' && c >= '0') {
130  if (valueIndex == miliSecondsIndex) {
131  miliSeconds += (c - '0') * miliSecondsFact;
132  miliSecondsFact /= 10;
133  } else {
134  *valueIndex *= 10;
135  *valueIndex += c - '0';
136  }
137  } else if (c == 'T') {
138  if (++valueIndex != hourIndex) {
139  throw ConversionException("\"T\" expected before hour");
140  }
141  } else if (c == '-') {
142  if (valueIndex < dayIndex) {
143  ++valueIndex;
144  } else {
145  throw ConversionException("unexpected \"-\" after day");
146  }
147  } else if (c == '.') {
148  if (valueIndex != secondsIndex) {
149  throw ConversionException("unexpected \".\"");
150  } else {
151  ++valueIndex;
152  }
153  } else if (c == ':') {
154  if (valueIndex < hourIndex) {
155  throw ConversionException("unexpected \":\" before hour");
156  } else if (valueIndex == secondsIndex) {
157  throw ConversionException("unexpected \":\" after second");
158  } else {
159  ++valueIndex;
160  }
161  } else if ((c == '+') && (++valueIndex == deltaHourIndex)) {
162  deltaNegative = false;
163  } else if ((c == '-') && (++valueIndex == deltaHourIndex)) {
164  deltaNegative = true;
165  } else if (c == '\0') {
166  break;
167  } else {
168  throw ConversionException(argsToString("unexpected \"", c, '\"'));
169  }
170  }
171  deltaNegative && (*deltaHourIndex = -*deltaHourIndex);
172  return make_pair(DateTime::fromDateAndTime(values[0], values[1], *dayIndex, *hourIndex, values[4], *secondsIndex, miliSeconds),
173  TimeSpan::fromMinutes(*deltaHourIndex * 60 + values[8]));
174 }
175 
181 string DateTime::toString(DateTimeOutputFormat format, bool noMilliseconds) const
182 {
183  string result;
184  toString(result, format, noMilliseconds);
185  return result;
186 }
187 
193 void DateTime::toString(string &result, DateTimeOutputFormat format, bool noMilliseconds) const
194 {
195  stringstream s(stringstream::in | stringstream::out);
196  s << setfill('0');
197  if (format == DateTimeOutputFormat::DateTimeAndWeekday || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
198  s << printDayOfWeek(dayOfWeek(), format == DateTimeOutputFormat::DateTimeAndShortWeekday) << ' ';
199  if (format == DateTimeOutputFormat::DateOnly || format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday
200  || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
201  s << setw(4) << year() << '-' << setw(2) << month() << '-' << setw(2) << day();
202  if (format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday
203  || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
204  s << " ";
205  if (format == DateTimeOutputFormat::TimeOnly || format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday
206  || format == DateTimeOutputFormat::DateTimeAndShortWeekday) {
207  s << setw(2) << hour() << ':' << setw(2) << minute() << ':' << setw(2) << second();
208  int ms = millisecond();
209  if (!noMilliseconds && ms > 0) {
210  s << '.' << setw(3) << ms;
211  }
212  }
213  result = s.str();
214 }
215 
220 string DateTime::toIsoString(TimeSpan timeZoneDelta) const
221 {
222  stringstream s(stringstream::in | stringstream::out);
223  s << setfill('0');
224  s << setw(4) << year() << '-' << setw(2) << month() << '-' << setw(2) << day() << 'T' << setw(2) << hour() << ':' << setw(2) << minute() << ':'
225  << setw(2) << second() << '.' << setw(3) << millisecond();
226  if (!timeZoneDelta.isNull()) {
227  s << (timeZoneDelta.isNegative() ? '-' : '+');
228  s << setw(2) << timeZoneDelta.hours() << ':' << setw(2) << timeZoneDelta.minutes();
229  }
230  return s.str();
231 }
232 
240 const char *DateTime::printDayOfWeek(DayOfWeek dayOfWeek, bool abbreviation)
241 {
242  if (abbreviation) {
243  switch (dayOfWeek) {
244  case DayOfWeek::Monday:
245  return "Mon";
246  case DayOfWeek::Tuesday:
247  return "Tue";
248  case DayOfWeek::Wednesday:
249  return "Wed";
250  case DayOfWeek::Thursday:
251  return "Thu";
252  case DayOfWeek::Friday:
253  return "Fri";
254  case DayOfWeek::Saturday:
255  return "Sat";
256  case DayOfWeek::Sunday:
257  return "Sun";
258  }
259  } else {
260  switch (dayOfWeek) {
261  case DayOfWeek::Monday:
262  return "Monday";
263  case DayOfWeek::Tuesday:
264  return "Tuesday";
265  case DayOfWeek::Wednesday:
266  return "Wednesday";
267  case DayOfWeek::Thursday:
268  return "Thursday";
269  case DayOfWeek::Friday:
270  return "Friday";
271  case DayOfWeek::Saturday:
272  return "Saturday";
273  case DayOfWeek::Sunday:
274  return "Sunday";
275  }
276  }
277  return "";
278 }
279 
280 #if defined(PLATFORM_UNIX) && !defined(PLATFORM_MAC)
281 
285 DateTime DateTime::exactGmtNow()
286 {
287  struct timespec t;
288  clock_gettime(CLOCK_REALTIME, &t);
289  return DateTime(
290  DateTime::unixEpochStart().totalTicks() + static_cast<uint64>(t.tv_sec) * TimeSpan::m_ticksPerSecond + static_cast<uint64>(t.tv_nsec) / 100);
291 }
292 #endif
293 
297 uint64 DateTime::dateToTicks(int year, int month, int day)
298 {
299  if (!inRangeInclMax(year, 1, 9999)) {
300  throw ConversionException("year is out of range");
301  }
302  if (!inRangeInclMax(month, 1, 12)) {
303  throw ConversionException("month is out of range");
304  }
305  const int *daysToMonth = isLeapYear(year) ? m_daysToMonth366 : m_daysToMonth365;
306  int passedMonth = month - 1;
307  if (!inRangeInclMax(day, 1, daysToMonth[month] - daysToMonth[passedMonth])) {
308  throw ConversionException("day is out of range");
309  }
310  int passedYears = year - 1;
311  int passedDays = day - 1;
312  return (passedYears * m_daysPerYear + passedYears / 4 - passedYears / 100 + passedYears / 400 + daysToMonth[passedMonth] + passedDays)
313  * TimeSpan::m_ticksPerDay;
314 }
315 
319 uint64 DateTime::timeToTicks(int hour, int minute, int second, double millisecond)
320 {
321  if (!inRangeExclMax(hour, 0, 24)) {
322  throw ConversionException("hour is out of range");
323  }
324  if (!inRangeExclMax(minute, 0, 60)) {
325  throw ConversionException("minute is out of range");
326  }
327  if (!inRangeExclMax(second, 0, 60)) {
328  throw ConversionException("second is out of range");
329  }
330  if (!inRangeExclMax(millisecond, 0.0, 1000.0)) {
331  throw ConversionException("millisecond is out of range");
332  }
333  return (hour * TimeSpan::m_ticksPerHour) + (minute * TimeSpan::m_ticksPerMinute) + (second * TimeSpan::m_ticksPerSecond)
334  + (uint64)(millisecond * (double)TimeSpan::m_ticksPerMillisecond);
335 }
336 
341 int DateTime::getDatePart(DatePart part) const
342 {
343  int fullDays = m_ticks / TimeSpan::m_ticksPerDay;
344  int full400YearBlocks = fullDays / m_daysPer400Years;
345  int daysMinusFull400YearBlocks = fullDays - full400YearBlocks * m_daysPer400Years;
346  int full100YearBlocks = daysMinusFull400YearBlocks / m_daysPer100Years;
347  if (full100YearBlocks == 4) {
348  full100YearBlocks = 3;
349  }
350  int daysMinusFull100YearBlocks = daysMinusFull400YearBlocks - full100YearBlocks * m_daysPer100Years;
351  int full4YearBlocks = daysMinusFull100YearBlocks / m_daysPer4Years;
352  int daysMinusFull4YearBlocks = daysMinusFull100YearBlocks - full4YearBlocks * m_daysPer4Years;
353  int full1YearBlocks = daysMinusFull4YearBlocks / m_daysPerYear;
354  if (full1YearBlocks == 4) {
355  full1YearBlocks = 3;
356  }
357  if (part == DatePart::Year) {
358  return full400YearBlocks * 400 + full100YearBlocks * 100 + full4YearBlocks * 4 + full1YearBlocks + 1;
359  }
360  int restDays = daysMinusFull4YearBlocks - full1YearBlocks * m_daysPerYear;
361  if (part == DatePart::DayOfYear) { // day
362  return restDays + 1;
363  }
364  const int *daysToMonth = (full1YearBlocks == 3 && (full4YearBlocks != 24 || full100YearBlocks == 3)) ? m_daysToMonth366 : m_daysToMonth365;
365  int month = 1;
366  while (restDays >= daysToMonth[month]) {
367  ++month;
368  }
369  if (part == DatePart::Month) {
370  return month;
371  } else if (part == DatePart::Day) {
372  return restDays - daysToMonth[month - 1] + 1;
373  }
374  return 0;
375 }
constexpr bool isNegative() const
Returns ture if the time interval represented by the current TimeSpan class is negative.
Definition: timespan.h:354
Represents an instant in time, typically expressed as a date and time of day.
Definition: datetime.h:51
Contains classes providing a means for handling date and time information.
Definition: datetime.h:12
The ConversionException class is thrown by the various conversion functions of this library when a co...
Represents a time interval.
Definition: timespan.h:27
constexpr int hours() const
Gets the hours component of the time interval represented by the current TimeSpan class...
Definition: timespan.h:248
constexpr StringType argsToString(Args &&... args)
STL namespace.
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:346
constexpr int minutes() const
Gets the minutes component of the time interval represented by the current TimeSpan class...
Definition: timespan.h:240
DatePart
Specifies the date part.
Definition: datetime.h:44
Contains several functions providing conversions between different data types.
DateTimeOutputFormat
Specifies the output format.
Definition: datetime.h:18
bool inRangeExclMax(num1 val, num2 min, num3 max)
Definition: datetime.cpp:35
bool inRangeInclMax(num1 val, num2 min, num3 max)
Definition: datetime.cpp:30
DayOfWeek
Specifies the day of the week.
Definition: datetime.h:30