C++ Utilities 5.32.0
Useful C++ classes and routines such as argument parser, IO and conversion utilities
Loading...
Searching...
No Matches
datetime.cpp
Go to the documentation of this file.
1#include "./datetime.h"
2
5
6#include <iomanip>
7#include <sstream>
8
9#if !defined(CPP_UTILITIES_CHRONO_BASED_EXACT_TIME) && (defined(PLATFORM_MAC) || !defined(PLATFORM_UNIX))
10#define CPP_UTILITIES_CHRONO_BASED_EXACT_TIME
11#endif
12#ifdef CPP_UTILITIES_CHRONO_BASED_EXACT_TIME
13#include <chrono>
14#endif
15
16using namespace std;
17
18namespace CppUtilities {
19
20const int DateTime::m_daysPerYear = 365;
21const int DateTime::m_daysPer4Years = 1461;
22const int DateTime::m_daysPer100Years = 36524;
23const int DateTime::m_daysPer400Years = 146097;
24const int DateTime::m_daysTo1601 = 584388;
25const int DateTime::m_daysTo1899 = 693593;
26const int DateTime::m_daysTo10000 = 3652059;
27const int DateTime::m_daysToMonth365[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
28const int DateTime::m_daysToMonth366[13] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
29const int DateTime::m_daysInMonth365[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
30const int DateTime::m_daysInMonth366[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
31
32template <typename num1, typename num2, typename num3> constexpr bool inRangeInclMax(num1 val, num2 min, num3 max)
33{
34 return (val) >= (min) && (val) <= (max);
35}
36
37template <typename num1, typename num2, typename num3> constexpr bool inRangeExclMax(num1 val, num2 min, num3 max)
38{
39 return (val) >= (min) && (val) < (max);
40}
41
59
71
76{
77 if (timeStamp) {
78 struct tm *const timeinfo = localtime(&timeStamp);
79 return DateTime::fromDateAndTime(timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min,
80 timeinfo->tm_sec < 60 ? timeinfo->tm_sec : 59, 0);
81 } else {
82 return DateTime();
83 }
84}
85
96{
98}
99
111std::pair<DateTime, TimeSpan> DateTime::fromIsoString(const char *str)
112{
113 const auto expr = DateTimeExpression::fromIsoString(str);
114 return std::make_pair(expr.value, expr.delta);
115}
116
122void DateTime::toString(string &result, DateTimeOutputFormat format, bool noMilliseconds) const
123{
124 if (format == DateTimeOutputFormat::Iso) {
125 result = toIsoString();
126 return;
127 }
128
129 stringstream s(stringstream::in | stringstream::out);
130 s << setfill('0');
131
133 constexpr auto dateDelimiter = '-', timeDelimiter = ':';
134 const int components[] = { year(), month(), day(), hour(), minute(), second(), millisecond(), microsecond(), nanosecond() };
135 const int *const firstTimeComponent = components + 3;
136 const int *const firstFractionalComponent = components + 6;
137 const int *const lastComponent = components + 8;
138 const int *componentsEnd = noMilliseconds ? firstFractionalComponent : lastComponent + 1;
139 for (const int *i = componentsEnd - 1; i > components; --i) {
140 if (i >= firstTimeComponent && *i == 0) {
141 componentsEnd = i;
142 } else if (i < firstTimeComponent && *i == 1) {
143 componentsEnd = i;
144 }
145 }
146 for (const int *i = components; i != componentsEnd; ++i) {
147 if (i == firstTimeComponent) {
148 s << 'T';
149 } else if (i == firstFractionalComponent) {
150 s << '.';
151 }
152 if (i == components) {
153 s << setw(4) << *i;
154 } else if (i < firstFractionalComponent) {
155 if (i < firstTimeComponent) {
156 s << dateDelimiter;
157 } else if (i > firstTimeComponent) {
158 s << timeDelimiter;
159 }
160 s << setw(2) << *i;
161 } else if (i < lastComponent) {
162 s << setw(3) << *i;
163 } else {
165 }
166 }
167 result = s.str();
168 return;
169 }
170
175 s << setw(4) << year() << '-' << setw(2) << month() << '-' << setw(2) << day();
178 s << " ";
181 s << setw(2) << hour() << ':' << setw(2) << minute() << ':' << setw(2) << second();
182 int ms = millisecond();
183 if (!noMilliseconds && ms > 0) {
184 s << '.' << setw(3) << ms;
185 }
186 }
187 result = s.str();
188}
189
194string DateTime::toIsoStringWithCustomDelimiters(TimeSpan timeZoneDelta, char dateDelimiter, char timeDelimiter, char timeZoneDelimiter) const
195{
196 stringstream s(stringstream::in | stringstream::out);
197 s << setfill('0');
198 s << setw(4) << year() << dateDelimiter << setw(2) << month() << dateDelimiter << setw(2) << day() << 'T' << setw(2) << hour() << timeDelimiter
199 << setw(2) << minute() << timeDelimiter << setw(2) << second();
200 const int milli(millisecond());
201 const int micro(microsecond());
202 const int nano(nanosecond());
203 if (milli || micro || nano) {
204 s << '.' << setw(3) << milli;
205 if (micro || nano) {
206 s << setw(3) << micro;
207 if (nano) {
209 }
210 }
211 }
212 if (!timeZoneDelta.isNull()) {
213 if (timeZoneDelta.isNegative()) {
214 s << '-';
215 timeZoneDelta = TimeSpan(-timeZoneDelta.totalTicks());
216 } else {
217 s << '+';
218 }
219 s << setw(2) << timeZoneDelta.hours() << timeZoneDelimiter << setw(2) << timeZoneDelta.minutes();
220 }
221 return s.str();
222}
223
228string DateTime::toIsoString(TimeSpan timeZoneDelta) const
229{
230 return toIsoStringWithCustomDelimiters(timeZoneDelta);
231}
232
240const char *DateTime::printDayOfWeek(DayOfWeek dayOfWeek, bool abbreviation)
241{
242 if (abbreviation) {
243 switch (dayOfWeek) {
245 return "Mon";
247 return "Tue";
249 return "Wed";
251 return "Thu";
253 return "Fri";
255 return "Sat";
257 return "Sun";
258 }
259 } else {
260 switch (dayOfWeek) {
262 return "Monday";
264 return "Tuesday";
266 return "Wednesday";
268 return "Thursday";
270 return "Friday";
272 return "Saturday";
274 return "Sunday";
275 }
276 }
277 return "";
278}
279
284{
285#ifdef CPP_UTILITIES_CHRONO_BASED_EXACT_TIME
287 + static_cast<std::uint64_t>(
288 std::chrono::time_point_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now()).time_since_epoch().count())
289 / 100);
290#else
291 struct timespec t;
292 clock_gettime(CLOCK_REALTIME, &t);
293 return DateTime(DateTime::unixEpochStart().totalTicks() + static_cast<std::uint64_t>(t.tv_sec) * TimeSpan::ticksPerSecond
294 + static_cast<std::uint64_t>(t.tv_nsec) / 100);
295#endif
296}
297
301DateTime::TickType DateTime::dateToTicks(int year, int month, int day)
302{
303 if (!inRangeInclMax(year, 1, 9999)) {
304 throw ConversionException("year is out of range");
305 }
306 if (!inRangeInclMax(month, 1, 12)) {
307 throw ConversionException("month is out of range");
308 }
309 const auto *const daysToMonth = reinterpret_cast<const int *>(isLeapYear(year) ? m_daysToMonth366 : m_daysToMonth365);
310 const int passedMonth = month - 1;
311 if (!inRangeInclMax(day, 1, daysToMonth[month] - daysToMonth[passedMonth])) {
312 throw ConversionException("day is out of range");
313 }
314 const auto passedYears = static_cast<unsigned int>(year - 1);
315 const auto passedDays = static_cast<unsigned int>(day - 1);
316 return (passedYears * m_daysPerYear + passedYears / 4 - passedYears / 100 + passedYears / 400
317 + static_cast<unsigned int>(daysToMonth[passedMonth]) + passedDays)
319}
320
324DateTime::TickType DateTime::timeToTicks(int hour, int minute, int second, double millisecond)
325{
326 if (!inRangeExclMax(hour, 0, 24)) {
327 throw ConversionException("hour is out of range");
328 }
329 if (!inRangeExclMax(minute, 0, 60)) {
330 throw ConversionException("minute is out of range");
331 }
332 if (!inRangeExclMax(second, 0, 60)) {
333 throw ConversionException("second is out of range");
334 }
335 if (!inRangeExclMax(millisecond, 0.0, 1000.0)) {
336 throw ConversionException("millisecond is out of range");
337 }
338 return static_cast<std::uint64_t>(hour * TimeSpan::ticksPerHour) + static_cast<std::uint64_t>(minute * TimeSpan::ticksPerMinute)
339 + static_cast<std::uint64_t>(second * TimeSpan::ticksPerSecond) + static_cast<std::uint64_t>(millisecond * TimeSpan::ticksPerMillisecond);
340}
341
346int DateTime::getDatePart(DatePart part) const
347{
348 const auto fullDays = static_cast<int>(m_ticks / TimeSpan::ticksPerDay);
349 const auto full400YearBlocks = fullDays / m_daysPer400Years;
350 const auto daysMinusFull400YearBlocks = fullDays - full400YearBlocks * m_daysPer400Years;
351 auto full100YearBlocks = daysMinusFull400YearBlocks / m_daysPer100Years;
352 if (full100YearBlocks == 4) {
353 full100YearBlocks = 3;
354 }
355 const auto daysMinusFull100YearBlocks = daysMinusFull400YearBlocks - full100YearBlocks * m_daysPer100Years;
356 const auto full4YearBlocks = daysMinusFull100YearBlocks / m_daysPer4Years;
357 const auto daysMinusFull4YearBlocks = daysMinusFull100YearBlocks - full4YearBlocks * m_daysPer4Years;
358 auto full1YearBlocks = daysMinusFull4YearBlocks / m_daysPerYear;
359 if (full1YearBlocks == 4) {
360 full1YearBlocks = 3;
361 }
362 if (part == DatePart::Year) {
363 return full400YearBlocks * 400 + full100YearBlocks * 100 + full4YearBlocks * 4 + full1YearBlocks + 1;
364 }
365 const auto restDays = daysMinusFull4YearBlocks - full1YearBlocks * m_daysPerYear;
366 if (part == DatePart::DayOfYear) { // day
367 return restDays + 1;
368 }
369 const auto *const daysToMonth = (full1YearBlocks == 3 && (full4YearBlocks != 24 || full100YearBlocks == 3)) ? m_daysToMonth366 : m_daysToMonth365;
370 auto month = 1;
371 while (restDays >= daysToMonth[month]) {
372 ++month;
373 }
374 if (part == DatePart::Month) {
375 return month;
376 } else if (part == DatePart::Day) {
377 return restDays - daysToMonth[month - 1] + 1;
378 }
379 return 0;
380}
381
383static DateTimeParts dateTimePartsFromParsingDistance(const int *valueIndex, const int *values)
384{
385 return static_cast<DateTimeParts>((1 << (valueIndex - values + 1)) - 1);
386}
388
399{
400 auto res = DateTimeExpression();
401 int values[9] = { 0 };
402 int *const yearIndex = values + 0;
403 int *const monthIndex = values + 1;
404 int *const dayIndex = values + 2;
405 int *const hourIndex = values + 3;
406 int *const secondsIndex = values + 5;
407 int *const miliSecondsIndex = values + 6;
408 int *const deltaHourIndex = values + 7;
409 int *const valuesEnd = values + 9;
410 int *valueIndex = values;
411 unsigned int remainingDigits = 4;
412 bool deltaNegative = false;
413 double millisecondsFact = 100.0, milliseconds = 0.0;
414 for (const char *strIndex = str;; ++strIndex) {
415 const char c = *strIndex;
416 if (c <= '9' && c >= '0') {
417 if (valueIndex == miliSecondsIndex) {
418 milliseconds += (c - '0') * millisecondsFact;
419 millisecondsFact /= 10;
420 } else {
421 if (!remainingDigits) {
422 if (++valueIndex == miliSecondsIndex || valueIndex >= valuesEnd) {
423 throw ConversionException("Max. number of digits exceeded");
424 }
425 remainingDigits = 2;
426 }
427 *valueIndex *= 10;
428 *valueIndex += c - '0';
429 remainingDigits -= 1;
430 }
431 } else if (c == 'T') {
432 if (++valueIndex != hourIndex) {
433 throw ConversionException("\"T\" expected before hour");
434 }
435 remainingDigits = 2;
436 } else if (c == '-') {
437 if (valueIndex < dayIndex) {
438 ++valueIndex;
439 } else if (++valueIndex >= secondsIndex) {
440 valueIndex = deltaHourIndex;
441 deltaNegative = true;
442 } else {
443 throw ConversionException("Unexpected \"-\" after day");
444 }
445 remainingDigits = 2;
446 } else if (c == '.') {
447 if (valueIndex != secondsIndex) {
448 throw ConversionException("Unexpected \".\"");
449 } else {
450 ++valueIndex;
451 }
452 } else if (c == ':') {
453 if (valueIndex < hourIndex) {
454 throw ConversionException("Unexpected \":\" before hour");
455 } else if (valueIndex == secondsIndex) {
456 throw ConversionException("Unexpected \":\" after second");
457 } else {
458 ++valueIndex;
459 }
460 remainingDigits = 2;
461 } else if ((c == '+') && (++valueIndex >= secondsIndex)) {
462 valueIndex = deltaHourIndex;
463 deltaNegative = false;
464 remainingDigits = 2;
465 } else if ((c == 'Z') && (++valueIndex >= secondsIndex)) {
466 valueIndex = deltaHourIndex + 2;
467 remainingDigits = 2;
468 } else if (c == '\0') {
469 break;
470 } else {
471 throw ConversionException(argsToString("Unexpected \"", c, '\"'));
472 }
473 }
474 res.delta = TimeSpan::fromMinutes(*deltaHourIndex * 60 + values[8]);
475 if (deltaNegative) {
476 res.delta = TimeSpan(-res.delta.totalTicks());
477 }
478 if (valueIndex < monthIndex && !*monthIndex) {
479 *monthIndex = 1;
480 }
481 if (valueIndex < dayIndex && !*dayIndex) {
482 *dayIndex = 1;
483 }
484 res.value = DateTime::fromDateAndTime(*yearIndex, *monthIndex, *dayIndex, *hourIndex, values[4], *secondsIndex, milliseconds);
485 res.parts = dateTimePartsFromParsingDistance(valueIndex, values);
486 return res;
487}
488
499{
500 auto res = DateTimeExpression();
501 int values[7] = { 0 };
502 int *const monthIndex = values + 1;
503 int *const dayIndex = values + 2;
504 int *const secondsIndex = values + 5;
505 int *valueIndex = values;
506 int *const valuesEnd = values + 7;
507 double millisecondsFact = 100.0, milliseconds = 0.0;
508 for (const char *strIndex = str;; ++strIndex) {
509 const char c = *strIndex;
510 if (c <= '9' && c >= '0') {
511 if (valueIndex > secondsIndex) {
512 milliseconds += (c - '0') * millisecondsFact;
513 millisecondsFact /= 10;
514 } else {
515 Detail::raiseAndAdd(*valueIndex, 10, c);
516 }
517 } else if ((c == '-' || c == ':' || c == '/') || (c == '.' && (valueIndex == secondsIndex))
518 || ((c == ' ' || c == 'T') && (valueIndex == dayIndex))) {
519 if (++valueIndex == valuesEnd) {
520 break; // just ignore further values for now
521 }
522 } else if (c == '\0') {
523 break;
524 } else {
525 throw ConversionException(argsToString("Unexpected character \"", c, '\"'));
526 }
527 }
528 if (valueIndex < monthIndex && !*monthIndex) {
529 *monthIndex = 1;
530 }
531 if (valueIndex < dayIndex && !*dayIndex) {
532 *dayIndex = 1;
533 }
534 res.value = DateTime::fromDateAndTime(values[0], values[1], *dayIndex, values[3], values[4], *secondsIndex, milliseconds);
535 res.parts = dateTimePartsFromParsingDistance(valueIndex, values);
536 return res;
537}
538
543std::string DateTimeExpression::toIsoString(char dateDelimiter, char timeDelimiter, char timeZoneDelimiter) const
544{
545 auto s = std::stringstream(std::stringstream::in | std::stringstream::out);
546 s << setfill('0');
547 if (parts && DateTimeParts::Year) {
548 s << setw(4) << value.year();
549 }
551 if (s.tellp()) {
552 s << dateDelimiter;
553 }
554 s << setw(2) << value.month();
555 }
556 if (parts && DateTimeParts::Day) {
557 if (s.tellp()) {
558 s << dateDelimiter;
559 }
560 s << setw(2) << value.day();
561 }
562 if (parts && DateTimeParts::Hour) {
563 if (s.tellp()) {
564 s << 'T';
565 }
566 s << setw(2) << value.hour();
567 }
569 if (s.tellp()) {
570 s << timeDelimiter;
571 }
572 s << setw(2) << value.minute();
573 }
575 if (s.tellp()) {
576 s << timeDelimiter;
577 }
578 s << setw(2) << value.second();
579 }
581 const auto milli = value.millisecond();
582 const auto micro = value.microsecond();
583 const auto nano = value.nanosecond();
584 s << '.' << setw(3) << milli;
585 if (micro || nano) {
586 s << setw(3) << micro;
587 if (nano) {
589 }
590 }
591 }
593 auto d = delta;
594 if (d.isNegative()) {
595 s << '-';
596 d = TimeSpan(-d.totalTicks());
597 } else {
598 s << '+';
599 }
601 s << setw(2) << d.hours();
602 }
605 s << timeZoneDelimiter;
606 }
607 s << setw(2) << d.minutes();
608 }
609 }
610 return s.str();
611}
612
613} // namespace CppUtilities
The ConversionException class is thrown by the various conversion functions of this library when a co...
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:466
int day() const
Returns the day component of the date represented by this instance.
Definition datetime.h:332
constexpr DayOfWeek dayOfWeek() const
Returns the day of the week represented by this instance.
Definition datetime.h:349
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:194
bool isLeapYear() const
Returns an indication whether the year represented by this instance is a leap year.
Definition datetime.h:424
int month() const
Returns the month component of the date represented by this instance.
Definition datetime.h:324
constexpr DateTime()
Constructs a DateTime.
Definition datetime.h:192
std::string toIsoString(TimeSpan timeZoneDelta=TimeSpan()) const
Returns the string representation of the current instance in the ISO format, eg.
Definition datetime.cpp:228
static constexpr DateTime unixEpochStart()
Returns the DateTime object for the "1970-01-01T00:00:00Z".
Definition datetime.h:492
std::uint64_t TickType
Definition datetime.h:57
constexpr int microsecond() const
Returns the microsecond component of the date represented by this instance.
Definition datetime.h:389
static std::pair< DateTime, TimeSpan > fromIsoString(const char *str)
Parses the specified ISO date time denotation provided as C-style string.
Definition datetime.cpp:111
static DateTime exactGmtNow()
Returns a DateTime object that is set to the current date and time on this computer,...
Definition datetime.cpp:283
constexpr int hour() const
Returns the hour component of the date represented by this instance.
Definition datetime.h:357
static DateTime fromString(const std::string &str)
Parses the given std::string as DateTime.
Definition datetime.h:242
constexpr int second() const
Returns the second component of the date represented by this instance.
Definition datetime.h:373
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:228
constexpr TickType totalTicks() const
Returns the number of ticks which represent the value of the current instance.
Definition datetime.h:308
static DateTime fromTimeStamp(std::time_t timeStamp)
Constructs a new DateTime object with the local time from the specified UNIX timeStamp.
Definition datetime.cpp:75
constexpr int millisecond() const
Returns the millisecond component of the date represented by this instance.
Definition datetime.h:381
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:240
constexpr int nanosecond() const
Returns the nanosecond component of the date represented by this instance.
Definition datetime.h:399
constexpr int minute() const
Returns the minute component of the date represented by this instance.
Definition datetime.h:365
int year() const
Returns the year component of the date represented by this instance.
Definition datetime.h:316
Represents a time interval.
Definition timespan.h:25
constexpr bool isNull() const
Returns true if the time interval represented by the current TimeSpan class is null.
Definition timespan.h:538
static constexpr TickType nanosecondsPerTick
Definition timespan.h:99
static constexpr TickType ticksPerMillisecond
Definition timespan.h:101
constexpr int minutes() const
Returns the minutes component of the time interval represented by the current TimeSpan class.
Definition timespan.h:339
constexpr TickType totalTicks() const
Returns the number of ticks that represent the value of the current TimeSpan class.
Definition timespan.h:249
static constexpr TickType ticksPerMinute
Definition timespan.h:103
static constexpr TimeSpan fromMinutes(double minutes)
Constructs a new instance of the TimeSpan class with the specified number of minutes.
Definition timespan.h:146
static constexpr TickType ticksPerSecond
Definition timespan.h:102
static constexpr TickType ticksPerDay
Definition timespan.h:105
static constexpr TickType ticksPerHour
Definition timespan.h:104
constexpr bool isNegative() const
Returns true if the time interval represented by the current TimeSpan class is negative.
Definition timespan.h:546
constexpr int hours() const
Returns the hours component of the time interval represented by the current TimeSpan class.
Definition timespan.h:347
Contains all utilities provided by the c++utilities library.
DatePart
Specifies the date part.
Definition datetime.h:48
constexpr bool inRangeExclMax(num1 val, num2 min, num3 max)
Definition datetime.cpp:37
constexpr bool inRangeInclMax(num1 val, num2 min, num3 max)
Definition datetime.cpp:32
StringType argsToString(Args &&...args)
constexpr T max(T first, T second)
Returns the greatest of the given items.
Definition math.h:96
DateTimeParts
The DateTimeParts enum specifies which parts of a timestamp are present.
Definition datetime.h:143
DateTimeOutputFormat
Specifies the output format.
Definition datetime.h:19
constexpr T min(T first, T second)
Returns the smallest of the given items.
Definition math.h:84
DayOfWeek
Specifies the day of the week.
Definition datetime.h:33
STL namespace.
The DateTimeExpression struct holds information about a time expression (e.g.
Definition datetime.h:161
static DateTimeExpression fromString(const char *str)
Parses the given C-style string.
Definition datetime.cpp:498
std::string toIsoString(char dateDelimiter='-', char timeDelimiter=':', char timeZoneDelimiter=':') const
Returns the string representation of the current instance in the ISO format.
Definition datetime.cpp:543
static DateTimeExpression fromIsoString(const char *str)
Parses the specified ISO date time denotation provided as C-style string.
Definition datetime.cpp:398
constexpr int i