C++ Utilities 5.11.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
10using namespace std;
11
12namespace CppUtilities {
13
14const int DateTime::m_daysPerYear = 365;
15const int DateTime::m_daysPer4Years = 1461;
16const int DateTime::m_daysPer100Years = 36524;
17const int DateTime::m_daysPer400Years = 146097;
18const int DateTime::m_daysTo1601 = 584388;
19const int DateTime::m_daysTo1899 = 693593;
20const int DateTime::m_daysTo10000 = 3652059;
21const int DateTime::m_daysToMonth365[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
22const int DateTime::m_daysToMonth366[13] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
23const int DateTime::m_daysInMonth365[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
24const int DateTime::m_daysInMonth366[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
25
26template <typename num1, typename num2, typename num3> constexpr bool inRangeInclMax(num1 val, num2 min, num3 max)
27{
28 return (val) >= (min) && (val) <= (max);
29}
30
31template <typename num1, typename num2, typename num3> constexpr bool inRangeExclMax(num1 val, num2 min, num3 max)
32{
33 return (val) >= (min) && (val) < (max);
34}
35
60{
61 if (timeStamp) {
62 struct tm *const 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
80{
81 int values[6] = { 0 };
82 int *const dayIndex = values + 2;
83 int *const secondsIndex = values + 5;
84 int *valueIndex = values;
85 int *const valuesEnd = values + 7;
86 double millisecondsFact = 100.0, milliseconds = 0.0;
87 for (const char *strIndex = str;; ++strIndex) {
88 const char c = *strIndex;
89 if (c <= '9' && c >= '0') {
90 if (valueIndex > secondsIndex) {
91 milliseconds += (c - '0') * millisecondsFact;
92 millisecondsFact /= 10;
93 } else {
94 Detail::raiseAndAdd(*valueIndex, 10, c);
95 }
96 } else if ((c == '-' || c == ':' || c == '/') || (c == '.' && (valueIndex == secondsIndex))
97 || ((c == ' ' || c == 'T') && (valueIndex == dayIndex))) {
98 if (++valueIndex == valuesEnd) {
99 break; // just ignore further values for now
100 }
101 } else if (c == '\0') {
102 break;
103 } else {
104 throw ConversionException(argsToString("Unexpected character \"", c, '\"'));
105 }
106 }
107 return DateTime::fromDateAndTime(values[0], values[1], *dayIndex, values[3], values[4], *secondsIndex, milliseconds);
108}
109
120std::pair<DateTime, TimeSpan> DateTime::fromIsoString(const char *str)
121{
122 int values[9] = { 0 };
123 int *const yearIndex = values + 0;
124 int *const monthIndex = values + 1;
125 int *const dayIndex = values + 2;
126 int *const hourIndex = values + 3;
127 int *const secondsIndex = values + 5;
128 int *const miliSecondsIndex = values + 6;
129 int *const deltaHourIndex = values + 7;
130 int *const valuesEnd = values + 9;
131 int *valueIndex = values;
132 unsigned int remainingDigits = 4;
133 bool deltaNegative = false;
134 double millisecondsFact = 100.0, milliseconds = 0.0;
135 for (const char *strIndex = str;; ++strIndex) {
136 const char c = *strIndex;
137 if (c <= '9' && c >= '0') {
138 if (valueIndex == miliSecondsIndex) {
139 milliseconds += (c - '0') * millisecondsFact;
140 millisecondsFact /= 10;
141 } else {
142 if (!remainingDigits) {
143 if (++valueIndex == miliSecondsIndex || valueIndex >= valuesEnd) {
144 throw ConversionException("Max. number of digits exceeded");
145 }
146 remainingDigits = 2;
147 }
148 *valueIndex *= 10;
149 *valueIndex += c - '0';
150 remainingDigits -= 1;
151 }
152 } else if (c == 'T') {
153 if (++valueIndex != hourIndex) {
154 throw ConversionException("\"T\" expected before hour");
155 }
156 remainingDigits = 2;
157 } else if (c == '-') {
158 if (valueIndex < dayIndex) {
159 ++valueIndex;
160 } else if (++valueIndex >= secondsIndex) {
161 valueIndex = deltaHourIndex;
162 deltaNegative = true;
163 } else {
164 throw ConversionException("Unexpected \"-\" after day");
165 }
166 remainingDigits = 2;
167 } else if (c == '.') {
168 if (valueIndex != secondsIndex) {
169 throw ConversionException("Unexpected \".\"");
170 } else {
171 ++valueIndex;
172 }
173 } else if (c == ':') {
174 if (valueIndex < hourIndex) {
175 throw ConversionException("Unexpected \":\" before hour");
176 } else if (valueIndex == secondsIndex) {
177 throw ConversionException("Unexpected \":\" after second");
178 } else {
179 ++valueIndex;
180 }
181 remainingDigits = 2;
182 } else if ((c == '+') && (++valueIndex >= secondsIndex)) {
183 valueIndex = deltaHourIndex;
184 deltaNegative = false;
185 remainingDigits = 2;
186 } else if ((c == 'Z') && (++valueIndex >= secondsIndex)) {
187 valueIndex = deltaHourIndex + 2;
188 remainingDigits = 2;
189 } else if (c == '\0') {
190 break;
191 } else {
192 throw ConversionException(argsToString("Unexpected \"", c, '\"'));
193 }
194 }
195 auto delta = TimeSpan::fromMinutes(*deltaHourIndex * 60 + values[8]);
196 if (deltaNegative) {
197 delta = TimeSpan(-delta.totalTicks());
198 }
199 if (valueIndex < monthIndex && !*monthIndex) {
200 *monthIndex = 1;
201 }
202 if (valueIndex < dayIndex && !*dayIndex) {
203 *dayIndex = 1;
204 }
205 return make_pair(DateTime::fromDateAndTime(*yearIndex, *monthIndex, *dayIndex, *hourIndex, values[4], *secondsIndex, milliseconds), delta);
206}
207
213void DateTime::toString(string &result, DateTimeOutputFormat format, bool noMilliseconds) const
214{
215 if (format == DateTimeOutputFormat::Iso) {
216 result = toIsoString();
217 return;
218 }
219
220 stringstream s(stringstream::in | stringstream::out);
221 s << setfill('0');
222
224 constexpr auto dateDelimiter = '-', timeDelimiter = ':';
225 const int components[] = { year(), month(), day(), hour(), minute(), second(), millisecond(), microsecond(), nanosecond() };
226 const int *const firstTimeComponent = components + 3;
227 const int *const firstFractionalComponent = components + 6;
228 const int *const lastComponent = components + 8;
229 const int *componentsEnd = noMilliseconds ? firstFractionalComponent : lastComponent + 1;
230 for (const int *i = componentsEnd - 1; i > components; --i) {
231 if (i >= firstTimeComponent && *i == 0) {
232 componentsEnd = i;
233 } else if (i < firstTimeComponent && *i == 1) {
234 componentsEnd = i;
235 }
236 }
237 for (const int *i = components; i != componentsEnd; ++i) {
238 if (i == firstTimeComponent) {
239 s << 'T';
240 } else if (i == firstFractionalComponent) {
241 s << '.';
242 }
243 if (i == components) {
244 s << setw(4) << *i;
245 } else if (i < firstFractionalComponent) {
246 if (i < firstTimeComponent) {
247 s << dateDelimiter;
248 } else if (i > firstTimeComponent) {
249 s << timeDelimiter;
250 }
251 s << setw(2) << *i;
252 } else if (i < lastComponent) {
253 s << setw(3) << *i;
254 } else {
256 }
257 }
258 result = s.str();
259 return;
260 }
261
266 s << setw(4) << year() << '-' << setw(2) << month() << '-' << setw(2) << day();
269 s << " ";
272 s << setw(2) << hour() << ':' << setw(2) << minute() << ':' << setw(2) << second();
273 int ms = millisecond();
274 if (!noMilliseconds && ms > 0) {
275 s << '.' << setw(3) << ms;
276 }
277 }
278 result = s.str();
279}
280
285string DateTime::toIsoStringWithCustomDelimiters(TimeSpan timeZoneDelta, char dateDelimiter, char timeDelimiter, char timeZoneDelimiter) const
286{
287 stringstream s(stringstream::in | stringstream::out);
288 s << setfill('0');
289 s << setw(4) << year() << dateDelimiter << setw(2) << month() << dateDelimiter << setw(2) << day() << 'T' << setw(2) << hour() << timeDelimiter
290 << setw(2) << minute() << timeDelimiter << setw(2) << second();
291 const int milli(millisecond());
292 const int micro(microsecond());
293 const int nano(nanosecond());
294 if (milli || micro || nano) {
295 s << '.' << setw(3) << milli;
296 if (micro || nano) {
297 s << setw(3) << micro;
298 if (nano) {
300 }
301 }
302 }
303 if (!timeZoneDelta.isNull()) {
304 if (timeZoneDelta.isNegative()) {
305 s << '-';
306 timeZoneDelta = TimeSpan(-timeZoneDelta.totalTicks());
307 } else {
308 s << '+';
309 }
310 s << setw(2) << timeZoneDelta.hours() << timeZoneDelimiter << setw(2) << timeZoneDelta.minutes();
311 }
312 return s.str();
313}
314
319string DateTime::toIsoString(TimeSpan timeZoneDelta) const
320{
321 return toIsoStringWithCustomDelimiters(timeZoneDelta);
322}
323
331const char *DateTime::printDayOfWeek(DayOfWeek dayOfWeek, bool abbreviation)
332{
333 if (abbreviation) {
334 switch (dayOfWeek) {
336 return "Mon";
338 return "Tue";
340 return "Wed";
342 return "Thu";
344 return "Fri";
346 return "Sat";
348 return "Sun";
349 }
350 } else {
351 switch (dayOfWeek) {
353 return "Monday";
355 return "Tuesday";
357 return "Wednesday";
359 return "Thursday";
361 return "Friday";
363 return "Saturday";
365 return "Sunday";
366 }
367 }
368 return "";
369}
370
371#if defined(PLATFORM_UNIX) && !defined(PLATFORM_MAC)
376DateTime DateTime::exactGmtNow()
377{
378 struct timespec t;
379 clock_gettime(CLOCK_REALTIME, &t);
380 return DateTime(DateTime::unixEpochStart().totalTicks() + static_cast<std::uint64_t>(t.tv_sec) * TimeSpan::ticksPerSecond
381 + static_cast<std::uint64_t>(t.tv_nsec) / 100);
382}
383#endif
384
388std::uint64_t DateTime::dateToTicks(int year, int month, int day)
389{
390 if (!inRangeInclMax(year, 1, 9999)) {
391 throw ConversionException("year is out of range");
392 }
393 if (!inRangeInclMax(month, 1, 12)) {
394 throw ConversionException("month is out of range");
395 }
396 const auto *const daysToMonth = reinterpret_cast<const int *>(isLeapYear(year) ? m_daysToMonth366 : m_daysToMonth365);
397 const int passedMonth = month - 1;
398 if (!inRangeInclMax(day, 1, daysToMonth[month] - daysToMonth[passedMonth])) {
399 throw ConversionException("day is out of range");
400 }
401 const auto passedYears = static_cast<unsigned int>(year - 1);
402 const auto passedDays = static_cast<unsigned int>(day - 1);
403 return (passedYears * m_daysPerYear + passedYears / 4 - passedYears / 100 + passedYears / 400
404 + static_cast<unsigned int>(daysToMonth[passedMonth]) + passedDays)
406}
407
411std::uint64_t DateTime::timeToTicks(int hour, int minute, int second, double millisecond)
412{
413 if (!inRangeExclMax(hour, 0, 24)) {
414 throw ConversionException("hour is out of range");
415 }
416 if (!inRangeExclMax(minute, 0, 60)) {
417 throw ConversionException("minute is out of range");
418 }
419 if (!inRangeExclMax(second, 0, 60)) {
420 throw ConversionException("second is out of range");
421 }
422 if (!inRangeExclMax(millisecond, 0.0, 1000.0)) {
423 throw ConversionException("millisecond is out of range");
424 }
425 return static_cast<std::uint64_t>(hour * TimeSpan::ticksPerHour) + static_cast<std::uint64_t>(minute * TimeSpan::ticksPerMinute)
426 + static_cast<std::uint64_t>(second * TimeSpan::ticksPerSecond) + static_cast<std::uint64_t>(millisecond * TimeSpan::ticksPerMillisecond);
427}
428
433int DateTime::getDatePart(DatePart part) const
434{
435 const auto fullDays = static_cast<int>(m_ticks / TimeSpan::ticksPerDay);
436 const auto full400YearBlocks = fullDays / m_daysPer400Years;
437 const auto daysMinusFull400YearBlocks = fullDays - full400YearBlocks * m_daysPer400Years;
438 auto full100YearBlocks = daysMinusFull400YearBlocks / m_daysPer100Years;
439 if (full100YearBlocks == 4) {
440 full100YearBlocks = 3;
441 }
442 const auto daysMinusFull100YearBlocks = daysMinusFull400YearBlocks - full100YearBlocks * m_daysPer100Years;
443 const auto full4YearBlocks = daysMinusFull100YearBlocks / m_daysPer4Years;
444 const auto daysMinusFull4YearBlocks = daysMinusFull100YearBlocks - full4YearBlocks * m_daysPer4Years;
445 auto full1YearBlocks = daysMinusFull4YearBlocks / m_daysPerYear;
446 if (full1YearBlocks == 4) {
447 full1YearBlocks = 3;
448 }
449 if (part == DatePart::Year) {
450 return full400YearBlocks * 400 + full100YearBlocks * 100 + full4YearBlocks * 4 + full1YearBlocks + 1;
451 }
452 const auto restDays = daysMinusFull4YearBlocks - full1YearBlocks * m_daysPerYear;
453 if (part == DatePart::DayOfYear) { // day
454 return restDays + 1;
455 }
456 const auto *const daysToMonth = (full1YearBlocks == 3 && (full4YearBlocks != 24 || full100YearBlocks == 3)) ? m_daysToMonth366 : m_daysToMonth365;
457 auto month = 1;
458 while (restDays >= daysToMonth[month]) {
459 ++month;
460 }
461 if (part == DatePart::Month) {
462 return month;
463 } else if (part == DatePart::Day) {
464 return restDays - daysToMonth[month - 1] + 1;
465 }
466 return 0;
467}
468
469} // namespace CppUtilities
The ConversionException class is thrown by the various conversion functions of this library when a co...
Represents an instant in time, typically expressed as a date and time of day.
Definition: datetime.h:53
std::string toString(DateTimeOutputFormat format=DateTimeOutputFormat::DateAndTime, bool noMilliseconds=false) const
Returns the string representation of the current instance using the specified format.
Definition: datetime.h:416
int day() const
Returns the day component of the date represented by this instance.
Definition: datetime.h:282
constexpr DayOfWeek dayOfWeek() const
Returns the day of the week represented by this instance.
Definition: datetime.h:299
std::string toIsoStringWithCustomDelimiters(TimeSpan timeZoneDelta=TimeSpan(), char dateDelimiter='-', char timeDelimiter=':', char timeZoneDelimiter=':') const
Returns the string representation of the current instance in the ISO format with custom delimiters,...
Definition: datetime.cpp:285
bool isLeapYear() const
Returns an indication whether the year represented by this instance is a leap year.
Definition: datetime.h:374
int month() const
Returns the month component of the date represented by this instance.
Definition: datetime.h:274
constexpr std::uint64_t totalTicks() const
Returns the number of ticks which represent the value of the current instance.
Definition: datetime.h:258
constexpr DateTime()
Constructs a DateTime.
Definition: datetime.h:141
std::string toIsoString(TimeSpan timeZoneDelta=TimeSpan()) const
Returns the string representation of the current instance in the ISO format, eg.
Definition: datetime.cpp:319
static constexpr DateTime unixEpochStart()
Returns the DateTime object for the "1970-01-01T00:00:00Z".
Definition: datetime.h:442
constexpr int microsecond() const
Returns the microsecond component of the date represented by this instance.
Definition: datetime.h:339
static std::pair< DateTime, TimeSpan > fromIsoString(const char *str)
Parses the specified ISO date time denotation provided as C-style string.
Definition: datetime.cpp:120
constexpr int hour() const
Returns the hour component of the date represented by this instance.
Definition: datetime.h:307
static DateTime fromString(const std::string &str)
Parses the given std::string as DateTime.
Definition: datetime.h:191
constexpr int second() const
Returns the second component of the date represented by this instance.
Definition: datetime.h:323
static DateTime fromDateAndTime(int year=1, int month=1, int day=1, int hour=0, int minute=0, int second=0, double millisecond=0.0)
Constructs a DateTime to the specified year, month, day, hour, minute, second and millisecond.
Definition: datetime.h:177
static DateTime fromTimeStamp(std::time_t timeStamp)
Constructs a new DateTime object with the local time from the specified UNIX timeStamp.
Definition: datetime.cpp:59
constexpr int millisecond() const
Returns the millisecond component of the date represented by this instance.
Definition: datetime.h:331
static const char * printDayOfWeek(DayOfWeek dayOfWeek, bool abbreviation=false)
Returns the string representation as C-style string for the given day of week.
Definition: datetime.cpp:331
constexpr int nanosecond() const
Returns the nanosecond component of the date represented by this instance.
Definition: datetime.h:349
constexpr int minute() const
Returns the minute component of the date represented by this instance.
Definition: datetime.h:315
int year() const
Returns the year component of the date represented by this instance.
Definition: datetime.h:266
Represents a time interval.
Definition: timespan.h:25
static constexpr std::int64_t ticksPerMinute
Definition: timespan.h:86
constexpr bool isNull() const
Returns true if the time interval represented by the current TimeSpan class is null.
Definition: timespan.h:441
static constexpr std::int64_t ticksPerDay
Definition: timespan.h:88
constexpr std::int64_t totalTicks() const
Returns the number of ticks that represent the value of the current TimeSpan class.
Definition: timespan.h:190
static constexpr std::int64_t ticksPerSecond
Definition: timespan.h:85
constexpr int minutes() const
Returns the minutes component of the time interval represented by the current TimeSpan class.
Definition: timespan.h:280
static constexpr std::int64_t nanosecondsPerTick
Definition: timespan.h:82
static constexpr std::int64_t ticksPerHour
Definition: timespan.h:87
static constexpr TimeSpan fromMinutes(double minutes)
Constructs a new instance of the TimeSpan class with the specified number of minutes.
Definition: timespan.h:129
constexpr bool isNegative() const
Returns true if the time interval represented by the current TimeSpan class is negative.
Definition: timespan.h:449
static constexpr std::int64_t ticksPerMillisecond
Definition: timespan.h:84
constexpr int hours() const
Returns the hours component of the time interval represented by the current TimeSpan class.
Definition: timespan.h:288
Contains all utilities provides by the c++utilities library.
DatePart
Specifies the date part.
Definition: datetime.h:46
constexpr bool inRangeExclMax(num1 val, num2 min, num3 max)
Definition: datetime.cpp:31
constexpr bool inRangeInclMax(num1 val, num2 min, num3 max)
Definition: datetime.cpp:26
StringType argsToString(Args &&...args)
constexpr T max(T first, T second)
Returns the greatest of the given items.
Definition: math.h:100
DateTimeOutputFormat
Specifies the output format.
Definition: datetime.h:17
constexpr T min(T first, T second)
Returns the smallest of the given items.
Definition: math.h:88
DayOfWeek
Specifies the day of the week.
Definition: datetime.h:31
STL namespace.
constexpr int i