C++ Utilities  4.6.1
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/stringconversion.h"
4 #include "../conversion/stringbuilder.h"
5 
6 #include <sstream>
7 #include <iomanip>
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>
27 inline bool inRangeInclMax(num1 val, num2 min, num3 max)
28 {
29  return (val) >= (min) && (val) <= (max);
30 }
31 
32 template<typename num1, typename num2, typename num3>
33 inline bool inRangeExclMax(num1 val, num2 min, num3 max)
34 {
35  return (val) >= (min) && (val) < (max);
36 }
37 
54 DateTime DateTime::fromTimeStamp(time_t timeStamp)
55 {
56  if(timeStamp) {
57  struct tm *timeinfo = localtime(&timeStamp);
58  return DateTime::fromDateAndTime(timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday,
59  timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec < 60 ? timeinfo->tm_sec : 59, 0);
60  } else {
61  return DateTime();
62  }
63 }
64 
68 DateTime DateTime::fromTimeStampGmt(time_t timeStamp)
69 {
70  if(timeStamp) {
71  struct tm *timeinfo = gmtime(&timeStamp);
72  return DateTime::fromDateAndTime(timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday,
73  timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec < 60 ? timeinfo->tm_sec : 59, 0);
74  } else {
75  return DateTime();
76  }
77 }
78 
82 DateTime DateTime::fromString(const char *str)
83 {
84  int values[6] = {0};
85  int *const dayIndex = values + 2;
86  int *const secondsIndex = values + 5;
87  int *valueIndex = values;
88  int *const valuesEnd = values + 7;
89  double miliSecondsFact = 100.0, miliSeconds = 0.0;
90  for(const char *strIndex = str; ; ++strIndex) {
91  const char c = *strIndex;
92  if(c <= '9' && c >= '0') {
93  if(valueIndex > secondsIndex) {
94  miliSeconds += (c - '0') * miliSecondsFact;
95  miliSecondsFact /= 10;
96  } else {
97  *valueIndex *= 10;
98  *valueIndex += c - '0';
99  }
100  } else if((c == '-' || c == ':' || c == '/') || (c == '.' && (valueIndex == secondsIndex)) || (c == ' ' && (valueIndex == dayIndex))) {
101  if(++valueIndex == valuesEnd) {
102  break; // just ignore further values for now
103  }
104  } else if(c == '\0') {
105  break;
106  } else {
107  throw ConversionException("unexpected "s + c);
108  }
109  }
110  return DateTime::fromDateAndTime(values[0], values[1], *dayIndex, values[3], values[4], *secondsIndex, miliSeconds);
111 }
112 
120 std::pair<DateTime, TimeSpan> DateTime::fromIsoString(const char *str)
121 {
122  int values[9] = {0};
123  int *const dayIndex = values + 2;
124  int *const hourIndex = values + 3;
125  int *const secondsIndex = values + 5;
126  int *const miliSecondsIndex = values + 6;
127  int *const deltaHourIndex = values + 7;
128  int *valueIndex = values;
129  bool deltaNegative = false;
130  double miliSecondsFact = 100.0, miliSeconds = 0.0;
131  for(const char *strIndex = str; ; ++strIndex) {
132  const char c = *strIndex;
133  if(c <= '9' && c >= '0') {
134  if(valueIndex == miliSecondsIndex) {
135  miliSeconds += (c - '0') * miliSecondsFact;
136  miliSecondsFact /= 10;
137  } else {
138  *valueIndex *= 10;
139  *valueIndex += c - '0';
140  }
141  } else if(c == 'T') {
142  if(++valueIndex != hourIndex) {
143  throw ConversionException("\"T\" expected before hour");
144  }
145  } else if(c == '-') {
146  if(valueIndex < dayIndex) {
147  ++valueIndex;
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 == deltaHourIndex)) {
166  deltaNegative = false;
167  } else if((c == '-') && (++valueIndex == deltaHourIndex)) {
168  deltaNegative = true;
169  } else if(c == '\0') {
170  break;
171  } else {
172  throw ConversionException("unexpected \""s % c + '\"');
173  }
174  }
175  deltaNegative && (*deltaHourIndex = -*deltaHourIndex);
176  return make_pair(DateTime::fromDateAndTime(values[0], values[1], *dayIndex, *hourIndex, values[4], *secondsIndex, miliSeconds), TimeSpan::fromMinutes(*deltaHourIndex * 60 + values[8]));
177 }
178 
184 string DateTime::toString(DateTimeOutputFormat format, bool noMilliseconds) const
185 {
186  string result;
187  toString(result, format, noMilliseconds);
188  return result;
189 }
190 
196 void DateTime::toString(string &result, DateTimeOutputFormat format, bool noMilliseconds) const
197 {
198  stringstream s(stringstream::in | stringstream::out);
199  s << setfill('0');
200  if(format == DateTimeOutputFormat::DateTimeAndWeekday
201  || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
202  s << printDayOfWeek(dayOfWeek(), format == DateTimeOutputFormat::DateTimeAndShortWeekday) << ' ';
203  if(format == DateTimeOutputFormat::DateOnly
204  || format == DateTimeOutputFormat::DateAndTime
205  || format == DateTimeOutputFormat::DateTimeAndWeekday
206  || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
207  s << setw(4) << year() << '-' << setw(2) << month() << '-' << setw(2) << day();
208  if(format == DateTimeOutputFormat::DateAndTime
209  || format == DateTimeOutputFormat::DateTimeAndWeekday
210  || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
211  s << " ";
212  if(format == DateTimeOutputFormat::TimeOnly
213  || format == DateTimeOutputFormat::DateAndTime
214  || format == DateTimeOutputFormat::DateTimeAndWeekday
215  || format == DateTimeOutputFormat::DateTimeAndShortWeekday) {
216  s << setw(2) << hour() << ':' << setw(2) << minute() << ':' << setw(2) << second();
217  int ms = millisecond();
218  if(!noMilliseconds && ms > 0) {
219  s << '.' << setw(3) << ms;
220  }
221  }
222  result = s.str();
223 }
224 
229 string DateTime::toIsoString(TimeSpan timeZoneDelta) const
230 {
231  stringstream s(stringstream::in | stringstream::out);
232  s << setfill('0');
233  s << setw(4) << year() << '-' << setw(2) << month() << '-' << setw(2) << day()
234  << 'T' << setw(2) << hour() << ':' << setw(2) << minute() << ':' << setw(2) << second() << '.' << setw(3) << millisecond();
235  if(!timeZoneDelta.isNull()) {
236  s << (timeZoneDelta.isNegative() ? '-' : '+');
237  s << setw(2) << timeZoneDelta.hours() << ':' << setw(2) << timeZoneDelta.minutes();
238  }
239  return s.str();
240 }
241 
249 const char *DateTime::printDayOfWeek(DayOfWeek dayOfWeek, bool abbreviation)
250 {
251  if(abbreviation) {
252  switch(dayOfWeek) {
253  case DayOfWeek::Monday:
254  return "Mon";
255  case DayOfWeek::Tuesday:
256  return "Tue";
257  case DayOfWeek::Wednesday:
258  return "Wed";
259  case DayOfWeek::Thursday:
260  return "Thu";
261  case DayOfWeek::Friday:
262  return "Fri";
263  case DayOfWeek::Saturday:
264  return "Sat";
265  case DayOfWeek::Sunday:
266  return "Sun";
267  }
268  } else {
269  switch(dayOfWeek) {
270  case DayOfWeek::Monday:
271  return "Monday";
272  case DayOfWeek::Tuesday:
273  return "Tuesday";
274  case DayOfWeek::Wednesday:
275  return "Wednesday";
276  case DayOfWeek::Thursday:
277  return "Thursday";
278  case DayOfWeek::Friday:
279  return "Friday";
280  case DayOfWeek::Saturday:
281  return "Saturday";
282  case DayOfWeek::Sunday:
283  return "Sunday";
284  }
285  }
286  return "";
287 }
288 
292 uint64 DateTime::dateToTicks(int year, int month, int day)
293 {
294  if(inRangeInclMax(year, 1, 9999)) {
295  if(inRangeInclMax(month, 1, 12)) {
296  const int *daysToMonth = isLeapYear(year) ? m_daysToMonth366 : m_daysToMonth365;
297  int passedMonth = month - 1;
298  if(inRangeInclMax(day, 1, daysToMonth[month] - daysToMonth[passedMonth])) {
299  int passedYears = year - 1;
300  int passedDays = day - 1;
301  return (passedYears * m_daysPerYear + passedYears / 4 - passedYears / 100 + passedYears / 400 + daysToMonth[passedMonth] + passedDays) * TimeSpan::ticksPerDay;
302  } else {
303  throw ConversionException("day is out of range");
304  }
305  } else {
306  throw ConversionException("month is out of range");
307  }
308  } else {
309  throw ConversionException("year is out of range");
310  }
311  return 0;
312 }
313 
317 uint64 DateTime::timeToTicks(int hour, int minute, int second, double millisecond)
318 {
319  if(!inRangeExclMax(hour, 0, 24)) {
320  throw ConversionException("hour is out of range");
321  }
322  if(!inRangeExclMax(minute, 0, 60)) {
323  throw ConversionException("minute is out of range");
324  }
325  if(!inRangeExclMax(second, 0, 60)) {
326  throw ConversionException("second is out of range");
327  }
328  if(!inRangeExclMax(millisecond, 0.0, 1000.0)) {
329  throw ConversionException("millisecond is out of range");
330  }
331  return (hour * TimeSpan::ticksPerHour) + (minute * TimeSpan::ticksPerMinute) + (second * TimeSpan::ticksPerSecond) + (uint64)(millisecond * (double)TimeSpan::ticksPerMillisecond);
332 }
333 
338 int DateTime::getDatePart(DatePart part) const
339 {
340  int fullDays = m_ticks / TimeSpan::ticksPerDay;
341  int full400YearBlocks = fullDays / m_daysPer400Years;
342  int daysMinusFull400YearBlocks = fullDays - full400YearBlocks * m_daysPer400Years;
343  int full100YearBlocks = daysMinusFull400YearBlocks / m_daysPer100Years;
344  if(full100YearBlocks == 4) {
345  full100YearBlocks = 3;
346  }
347  int daysMinusFull100YearBlocks = daysMinusFull400YearBlocks - full100YearBlocks * m_daysPer100Years;
348  int full4YearBlocks = daysMinusFull100YearBlocks / m_daysPer4Years;
349  int daysMinusFull4YearBlocks = daysMinusFull100YearBlocks - full4YearBlocks * m_daysPer4Years;
350  int full1YearBlocks = daysMinusFull4YearBlocks / m_daysPerYear;
351  if(full1YearBlocks == 4) {
352  full1YearBlocks = 3;
353  }
354  if(part == DatePart::Year) {
355  return full400YearBlocks * 400 + full100YearBlocks * 100 + full4YearBlocks * 4 + full1YearBlocks + 1;
356  }
357  int restDays = daysMinusFull4YearBlocks - full1YearBlocks * m_daysPerYear;
358  if(part == DatePart::DayOfYear) { // day
359  return restDays + 1;
360  }
361  const int *daysToMonth = (full1YearBlocks == 3 && (full4YearBlocks != 24 || full100YearBlocks == 3)) ? m_daysToMonth366 : m_daysToMonth365;
362  int month = 1;
363  while(restDays >= daysToMonth[month]) {
364  ++month;
365  }
366  if(part == DatePart::Month) {
367  return month;
368  } else if(part == DatePart::Day) {
369  return restDays - daysToMonth[month - 1] + 1;
370  }
371  return 0;
372 }
constexpr bool isNegative() const
Returns ture if the time interval represented by the current TimeSpan class is negative.
Definition: timespan.h:343
Represents an instant in time, typically expressed as a date and time of day.
Definition: datetime.h:55
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:28
constexpr int hours() const
Gets the hours component of the time interval represented by the current TimeSpan class...
Definition: timespan.h:237
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:335
constexpr int minutes() const
Gets the minutes component of the time interval represented by the current TimeSpan class...
Definition: timespan.h:229
DatePart
Specifies the date part.
Definition: datetime.h:47
Contains several functions providing conversions between different data types.
DateTimeOutputFormat
Specifies the output format.
Definition: datetime.h:19
bool inRangeExclMax(num1 val, num2 min, num3 max)
Definition: datetime.cpp:33
bool inRangeInclMax(num1 val, num2 min, num3 max)
Definition: datetime.cpp:27
DayOfWeek
Specifies the day of the week.
Definition: datetime.h:32