C++ Utilities 5.22.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
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
72{
73 if (timeStamp) {
74 struct tm *const timeinfo = localtime(&timeStamp);
75 return DateTime::fromDateAndTime(timeinfo->tm_year + 1900, timeinfo->tm_mon + 1, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min,
76 timeinfo->tm_sec < 60 ? timeinfo->tm_sec : 59, 0);
77 } else {
78 return DateTime();
79 }
80}
81
92{
94}
95
107std::pair<DateTime, TimeSpan> DateTime::fromIsoString(const char *str)
108{
109 const auto expr = DateTimeExpression::fromIsoString(str);
110 return std::make_pair(expr.value, expr.delta);
111}
112
118void DateTime::toString(string &result, DateTimeOutputFormat format, bool noMilliseconds) const
119{
120 if (format == DateTimeOutputFormat::Iso) {
121 result = toIsoString();
122 return;
123 }
124
125 stringstream s(stringstream::in | stringstream::out);
126 s << setfill('0');
127
129 constexpr auto dateDelimiter = '-', timeDelimiter = ':';
130 const int components[] = { year(), month(), day(), hour(), minute(), second(), millisecond(), microsecond(), nanosecond() };
131 const int *const firstTimeComponent = components + 3;
132 const int *const firstFractionalComponent = components + 6;
133 const int *const lastComponent = components + 8;
134 const int *componentsEnd = noMilliseconds ? firstFractionalComponent : lastComponent + 1;
135 for (const int *i = componentsEnd - 1; i > components; --i) {
136 if (i >= firstTimeComponent && *i == 0) {
137 componentsEnd = i;
138 } else if (i < firstTimeComponent && *i == 1) {
139 componentsEnd = i;
140 }
141 }
142 for (const int *i = components; i != componentsEnd; ++i) {
143 if (i == firstTimeComponent) {
144 s << 'T';
145 } else if (i == firstFractionalComponent) {
146 s << '.';
147 }
148 if (i == components) {
149 s << setw(4) << *i;
150 } else if (i < firstFractionalComponent) {
151 if (i < firstTimeComponent) {
152 s << dateDelimiter;
153 } else if (i > firstTimeComponent) {
154 s << timeDelimiter;
155 }
156 s << setw(2) << *i;
157 } else if (i < lastComponent) {
158 s << setw(3) << *i;
159 } else {
161 }
162 }
163 result = s.str();
164 return;
165 }
166
171 s << setw(4) << year() << '-' << setw(2) << month() << '-' << setw(2) << day();
174 s << " ";
177 s << setw(2) << hour() << ':' << setw(2) << minute() << ':' << setw(2) << second();
178 int ms = millisecond();
179 if (!noMilliseconds && ms > 0) {
180 s << '.' << setw(3) << ms;
181 }
182 }
183 result = s.str();
184}
185
190string DateTime::toIsoStringWithCustomDelimiters(TimeSpan timeZoneDelta, char dateDelimiter, char timeDelimiter, char timeZoneDelimiter) const
191{
192 stringstream s(stringstream::in | stringstream::out);
193 s << setfill('0');
194 s << setw(4) << year() << dateDelimiter << setw(2) << month() << dateDelimiter << setw(2) << day() << 'T' << setw(2) << hour() << timeDelimiter
195 << setw(2) << minute() << timeDelimiter << setw(2) << second();
196 const int milli(millisecond());
197 const int micro(microsecond());
198 const int nano(nanosecond());
199 if (milli || micro || nano) {
200 s << '.' << setw(3) << milli;
201 if (micro || nano) {
202 s << setw(3) << micro;
203 if (nano) {
205 }
206 }
207 }
208 if (!timeZoneDelta.isNull()) {
209 if (timeZoneDelta.isNegative()) {
210 s << '-';
211 timeZoneDelta = TimeSpan(-timeZoneDelta.totalTicks());
212 } else {
213 s << '+';
214 }
215 s << setw(2) << timeZoneDelta.hours() << timeZoneDelimiter << setw(2) << timeZoneDelta.minutes();
216 }
217 return s.str();
218}
219
224string DateTime::toIsoString(TimeSpan timeZoneDelta) const
225{
226 return toIsoStringWithCustomDelimiters(timeZoneDelta);
227}
228
236const char *DateTime::printDayOfWeek(DayOfWeek dayOfWeek, bool abbreviation)
237{
238 if (abbreviation) {
239 switch (dayOfWeek) {
241 return "Mon";
243 return "Tue";
245 return "Wed";
247 return "Thu";
249 return "Fri";
251 return "Sat";
253 return "Sun";
254 }
255 } else {
256 switch (dayOfWeek) {
258 return "Monday";
260 return "Tuesday";
262 return "Wednesday";
264 return "Thursday";
266 return "Friday";
268 return "Saturday";
270 return "Sunday";
271 }
272 }
273 return "";
274}
275
276#if defined(PLATFORM_UNIX) && !defined(PLATFORM_MAC)
281DateTime DateTime::exactGmtNow()
282{
283 struct timespec t;
284 clock_gettime(CLOCK_REALTIME, &t);
285 return DateTime(DateTime::unixEpochStart().totalTicks() + static_cast<std::uint64_t>(t.tv_sec) * TimeSpan::ticksPerSecond
286 + static_cast<std::uint64_t>(t.tv_nsec) / 100);
287}
288#endif
289
293DateTime::TickType DateTime::dateToTicks(int year, int month, int day)
294{
295 if (!inRangeInclMax(year, 1, 9999)) {
296 throw ConversionException("year is out of range");
297 }
298 if (!inRangeInclMax(month, 1, 12)) {
299 throw ConversionException("month is out of range");
300 }
301 const auto *const daysToMonth = reinterpret_cast<const int *>(isLeapYear(year) ? m_daysToMonth366 : m_daysToMonth365);
302 const int passedMonth = month - 1;
303 if (!inRangeInclMax(day, 1, daysToMonth[month] - daysToMonth[passedMonth])) {
304 throw ConversionException("day is out of range");
305 }
306 const auto passedYears = static_cast<unsigned int>(year - 1);
307 const auto passedDays = static_cast<unsigned int>(day - 1);
308 return (passedYears * m_daysPerYear + passedYears / 4 - passedYears / 100 + passedYears / 400
309 + static_cast<unsigned int>(daysToMonth[passedMonth]) + passedDays)
311}
312
316DateTime::TickType DateTime::timeToTicks(int hour, int minute, int second, double millisecond)
317{
318 if (!inRangeExclMax(hour, 0, 24)) {
319 throw ConversionException("hour is out of range");
320 }
321 if (!inRangeExclMax(minute, 0, 60)) {
322 throw ConversionException("minute is out of range");
323 }
324 if (!inRangeExclMax(second, 0, 60)) {
325 throw ConversionException("second is out of range");
326 }
327 if (!inRangeExclMax(millisecond, 0.0, 1000.0)) {
328 throw ConversionException("millisecond is out of range");
329 }
330 return static_cast<std::uint64_t>(hour * TimeSpan::ticksPerHour) + static_cast<std::uint64_t>(minute * TimeSpan::ticksPerMinute)
331 + static_cast<std::uint64_t>(second * TimeSpan::ticksPerSecond) + static_cast<std::uint64_t>(millisecond * TimeSpan::ticksPerMillisecond);
332}
333
338int DateTime::getDatePart(DatePart part) const
339{
340 const auto fullDays = static_cast<int>(m_ticks / TimeSpan::ticksPerDay);
341 const auto full400YearBlocks = fullDays / m_daysPer400Years;
342 const auto daysMinusFull400YearBlocks = fullDays - full400YearBlocks * m_daysPer400Years;
343 auto full100YearBlocks = daysMinusFull400YearBlocks / m_daysPer100Years;
344 if (full100YearBlocks == 4) {
345 full100YearBlocks = 3;
346 }
347 const auto daysMinusFull100YearBlocks = daysMinusFull400YearBlocks - full100YearBlocks * m_daysPer100Years;
348 const auto full4YearBlocks = daysMinusFull100YearBlocks / m_daysPer4Years;
349 const auto daysMinusFull4YearBlocks = daysMinusFull100YearBlocks - full4YearBlocks * m_daysPer4Years;
350 auto 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 const auto restDays = daysMinusFull4YearBlocks - full1YearBlocks * m_daysPerYear;
358 if (part == DatePart::DayOfYear) { // day
359 return restDays + 1;
360 }
361 const auto *const daysToMonth = (full1YearBlocks == 3 && (full4YearBlocks != 24 || full100YearBlocks == 3)) ? m_daysToMonth366 : m_daysToMonth365;
362 auto 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}
373
375static DateTimeParts dateTimePartsFromParsingDistance(const int *valueIndex, const int *values)
376{
377 return static_cast<DateTimeParts>((1 << (valueIndex - values + 1)) - 1);
378}
380
391{
392 auto res = DateTimeExpression();
393 int values[9] = { 0 };
394 int *const yearIndex = values + 0;
395 int *const monthIndex = values + 1;
396 int *const dayIndex = values + 2;
397 int *const hourIndex = values + 3;
398 int *const secondsIndex = values + 5;
399 int *const miliSecondsIndex = values + 6;
400 int *const deltaHourIndex = values + 7;
401 int *const valuesEnd = values + 9;
402 int *valueIndex = values;
403 unsigned int remainingDigits = 4;
404 bool deltaNegative = false;
405 double millisecondsFact = 100.0, milliseconds = 0.0;
406 for (const char *strIndex = str;; ++strIndex) {
407 const char c = *strIndex;
408 if (c <= '9' && c >= '0') {
409 if (valueIndex == miliSecondsIndex) {
410 milliseconds += (c - '0') * millisecondsFact;
411 millisecondsFact /= 10;
412 } else {
413 if (!remainingDigits) {
414 if (++valueIndex == miliSecondsIndex || valueIndex >= valuesEnd) {
415 throw ConversionException("Max. number of digits exceeded");
416 }
417 remainingDigits = 2;
418 }
419 *valueIndex *= 10;
420 *valueIndex += c - '0';
421 remainingDigits -= 1;
422 }
423 } else if (c == 'T') {
424 if (++valueIndex != hourIndex) {
425 throw ConversionException("\"T\" expected before hour");
426 }
427 remainingDigits = 2;
428 } else if (c == '-') {
429 if (valueIndex < dayIndex) {
430 ++valueIndex;
431 } else if (++valueIndex >= secondsIndex) {
432 valueIndex = deltaHourIndex;
433 deltaNegative = true;
434 } else {
435 throw ConversionException("Unexpected \"-\" after day");
436 }
437 remainingDigits = 2;
438 } else if (c == '.') {
439 if (valueIndex != secondsIndex) {
440 throw ConversionException("Unexpected \".\"");
441 } else {
442 ++valueIndex;
443 }
444 } else if (c == ':') {
445 if (valueIndex < hourIndex) {
446 throw ConversionException("Unexpected \":\" before hour");
447 } else if (valueIndex == secondsIndex) {
448 throw ConversionException("Unexpected \":\" after second");
449 } else {
450 ++valueIndex;
451 }
452 remainingDigits = 2;
453 } else if ((c == '+') && (++valueIndex >= secondsIndex)) {
454 valueIndex = deltaHourIndex;
455 deltaNegative = false;
456 remainingDigits = 2;
457 } else if ((c == 'Z') && (++valueIndex >= secondsIndex)) {
458 valueIndex = deltaHourIndex + 2;
459 remainingDigits = 2;
460 } else if (c == '\0') {
461 break;
462 } else {
463 throw ConversionException(argsToString("Unexpected \"", c, '\"'));
464 }
465 }
466 res.delta = TimeSpan::fromMinutes(*deltaHourIndex * 60 + values[8]);
467 if (deltaNegative) {
468 res.delta = TimeSpan(-res.delta.totalTicks());
469 }
470 if (valueIndex < monthIndex && !*monthIndex) {
471 *monthIndex = 1;
472 }
473 if (valueIndex < dayIndex && !*dayIndex) {
474 *dayIndex = 1;
475 }
476 res.value = DateTime::fromDateAndTime(*yearIndex, *monthIndex, *dayIndex, *hourIndex, values[4], *secondsIndex, milliseconds);
477 res.parts = dateTimePartsFromParsingDistance(valueIndex, values);
478 return res;
479}
480
491{
492 auto res = DateTimeExpression();
493 int values[7] = { 0 };
494 int *const monthIndex = values + 1;
495 int *const dayIndex = values + 2;
496 int *const secondsIndex = values + 5;
497 int *valueIndex = values;
498 int *const valuesEnd = values + 7;
499 double millisecondsFact = 100.0, milliseconds = 0.0;
500 for (const char *strIndex = str;; ++strIndex) {
501 const char c = *strIndex;
502 if (c <= '9' && c >= '0') {
503 if (valueIndex > secondsIndex) {
504 milliseconds += (c - '0') * millisecondsFact;
505 millisecondsFact /= 10;
506 } else {
507 Detail::raiseAndAdd(*valueIndex, 10, c);
508 }
509 } else if ((c == '-' || c == ':' || c == '/') || (c == '.' && (valueIndex == secondsIndex))
510 || ((c == ' ' || c == 'T') && (valueIndex == dayIndex))) {
511 if (++valueIndex == valuesEnd) {
512 break; // just ignore further values for now
513 }
514 } else if (c == '\0') {
515 break;
516 } else {
517 throw ConversionException(argsToString("Unexpected character \"", c, '\"'));
518 }
519 }
520 if (valueIndex < monthIndex && !*monthIndex) {
521 *monthIndex = 1;
522 }
523 if (valueIndex < dayIndex && !*dayIndex) {
524 *dayIndex = 1;
525 }
526 res.value = DateTime::fromDateAndTime(values[0], values[1], *dayIndex, values[3], values[4], *secondsIndex, milliseconds);
527 res.parts = dateTimePartsFromParsingDistance(valueIndex, values);
528 return res;
529}
530
535std::string DateTimeExpression::toIsoString(char dateDelimiter, char timeDelimiter, char timeZoneDelimiter) const
536{
537 auto s = std::stringstream(std::stringstream::in | std::stringstream::out);
538 s << setfill('0');
540 s << setw(4) << value.year();
541 }
543 if (s.tellp()) {
544 s << dateDelimiter;
545 }
546 s << setw(2) << value.month();
547 }
549 if (s.tellp()) {
550 s << dateDelimiter;
551 }
552 s << setw(2) << value.day();
553 }
555 if (s.tellp()) {
556 s << 'T';
557 }
558 s << setw(2) << value.hour();
559 }
561 if (s.tellp()) {
562 s << timeDelimiter;
563 }
564 s << setw(2) << value.minute();
565 }
567 if (s.tellp()) {
568 s << timeDelimiter;
569 }
570 s << setw(2) << value.second();
571 }
573 const auto milli = value.millisecond();
574 const auto micro = value.microsecond();
575 const auto nano = value.nanosecond();
576 s << '.' << setw(3) << milli;
577 if (micro || nano) {
578 s << setw(3) << micro;
579 if (nano) {
581 }
582 }
583 }
585 auto d = delta;
586 if (d.isNegative()) {
587 s << '-';
588 d = TimeSpan(-d.totalTicks());
589 } else {
590 s << '+';
591 }
593 s << setw(2) << d.hours();
594 }
597 s << timeZoneDelimiter;
598 }
599 s << setw(2) << d.minutes();
600 }
601 }
602 return s.str();
603}
604
605} // 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:55
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:468
int day() const
Returns the day component of the date represented by this instance.
Definition: datetime.h:334
constexpr DayOfWeek dayOfWeek() const
Returns the day of the week represented by this instance.
Definition: datetime.h:351
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:190
bool isLeapYear() const
Returns an indication whether the year represented by this instance is a leap year.
Definition: datetime.h:426
int month() const
Returns the month component of the date represented by this instance.
Definition: datetime.h:326
constexpr DateTime()
Constructs a DateTime.
Definition: datetime.h:194
std::string toIsoString(TimeSpan timeZoneDelta=TimeSpan()) const
Returns the string representation of the current instance in the ISO format, eg.
Definition: datetime.cpp:224
static constexpr DateTime unixEpochStart()
Returns the DateTime object for the "1970-01-01T00:00:00Z".
Definition: datetime.h:494
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:391
static std::pair< DateTime, TimeSpan > fromIsoString(const char *str)
Parses the specified ISO date time denotation provided as C-style string.
Definition: datetime.cpp:107
constexpr int hour() const
Returns the hour component of the date represented by this instance.
Definition: datetime.h:359
static DateTime fromString(const std::string &str)
Parses the given std::string as DateTime.
Definition: datetime.h:244
constexpr int second() const
Returns the second component of the date represented by this instance.
Definition: datetime.h:375
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:230
constexpr TickType totalTicks() const
Returns the number of ticks which represent the value of the current instance.
Definition: datetime.h:310
static DateTime fromTimeStamp(std::time_t timeStamp)
Constructs a new DateTime object with the local time from the specified UNIX timeStamp.
Definition: datetime.cpp:71
constexpr int millisecond() const
Returns the millisecond component of the date represented by this instance.
Definition: datetime.h:383
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:236
constexpr int nanosecond() const
Returns the nanosecond component of the date represented by this instance.
Definition: datetime.h:401
constexpr int minute() const
Returns the minute component of the date represented by this instance.
Definition: datetime.h:367
int year() const
Returns the year component of the date represented by this instance.
Definition: datetime.h:318
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 provides 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: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
DateTimeParts
The DateTimeParts enum specifies which parts of a timestamp are present.
Definition: datetime.h:145
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:88
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:163
static DateTimeExpression fromString(const char *str)
Parses the given C-style string.
Definition: datetime.cpp:490
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:535
static DateTimeExpression fromIsoString(const char *str)
Parses the specified ISO date time denotation provided as C-style string.
Definition: datetime.cpp:390
constexpr int i