3 #include "../conversion/stringbuilder.h"
4 #include "../conversion/stringconversion.h"
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 };
26 template <
typename num1,
typename num2,
typename num3> constexpr
bool inRangeInclMax(num1 val, num2
min, num3
max)
28 return (val) >= (
min) && (val) <= (
max);
31 template <
typename num1,
typename num2,
typename num3> constexpr
bool inRangeExclMax(num1 val, num2
min, num3
max)
33 return (val) >= (
min) && (val) < (
max);
59 DateTime DateTime::fromTimeStamp(time_t 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);
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;
94 Detail::raiseAndAdd(*valueIndex, 10, c);
96 }
else if ((c ==
'-' || c ==
':' || c ==
'/') || (c ==
'.' && (valueIndex == secondsIndex))
97 || ((c ==
' ' || c ==
'T') && (valueIndex == dayIndex))) {
98 if (++valueIndex == valuesEnd) {
101 }
else if (c ==
'\0') {
107 return DateTime::fromDateAndTime(values[0], values[1], *dayIndex, values[3], values[4], *secondsIndex, milliseconds);
120 std::pair<DateTime, TimeSpan> DateTime::fromIsoString(
const char *str)
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;
142 if (!remainingDigits) {
143 if (++valueIndex == miliSecondsIndex || valueIndex >= valuesEnd) {
149 *valueIndex += c -
'0';
150 remainingDigits -= 1;
152 }
else if (c ==
'T') {
153 if (++valueIndex != hourIndex) {
157 }
else if (c ==
'-') {
158 if (valueIndex < dayIndex) {
160 }
else if (++valueIndex >= secondsIndex) {
161 valueIndex = deltaHourIndex;
162 deltaNegative =
true;
167 }
else if (c ==
'.') {
168 if (valueIndex != secondsIndex) {
173 }
else if (c ==
':') {
174 if (valueIndex < hourIndex) {
176 }
else if (valueIndex == secondsIndex) {
182 }
else if ((c ==
'+') && (++valueIndex >= secondsIndex)) {
183 valueIndex = deltaHourIndex;
184 deltaNegative =
false;
186 }
else if ((c ==
'Z') && (++valueIndex >= secondsIndex)) {
187 valueIndex = deltaHourIndex + 2;
189 }
else if (c ==
'\0') {
195 auto delta = TimeSpan::fromMinutes(*deltaHourIndex * 60 + values[8]);
197 delta =
TimeSpan(-delta.totalTicks());
199 if (valueIndex < monthIndex && !*monthIndex) {
202 if (valueIndex < dayIndex && !*dayIndex) {
205 return make_pair(DateTime::fromDateAndTime(*yearIndex, *monthIndex, *dayIndex, *hourIndex, values[4], *secondsIndex, milliseconds), delta);
215 if (format == DateTimeOutputFormat::Iso) {
216 result = toIsoString();
220 stringstream s(stringstream::in | stringstream::out);
223 if (format == DateTimeOutputFormat::IsoOmittingDefaultComponents) {
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) {
233 }
else if (
i < firstTimeComponent && *
i == 1) {
237 for (
const int *
i = components;
i != componentsEnd; ++
i) {
238 if (
i == firstTimeComponent) {
240 }
else if (
i == firstFractionalComponent) {
243 if (
i == components) {
245 }
else if (
i < firstFractionalComponent) {
246 if (
i < firstTimeComponent) {
248 }
else if (
i > firstTimeComponent) {
252 }
else if (
i < lastComponent) {
255 s << *
i / TimeSpan::nanosecondsPerTick;
262 if (format == DateTimeOutputFormat::DateTimeAndWeekday || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
263 s << printDayOfWeek(dayOfWeek(), format == DateTimeOutputFormat::DateTimeAndShortWeekday) <<
' ';
264 if (format == DateTimeOutputFormat::DateOnly || format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday
265 || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
266 s << setw(4) << year() <<
'-' << setw(2) << month() <<
'-' << setw(2) << day();
267 if (format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday
268 || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
270 if (format == DateTimeOutputFormat::TimeOnly || format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday
271 || format == DateTimeOutputFormat::DateTimeAndShortWeekday) {
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;
285 string DateTime::toIsoStringWithCustomDelimiters(
TimeSpan timeZoneDelta,
char dateDelimiter,
char timeDelimiter,
char timeZoneDelimiter)
const
287 stringstream s(stringstream::in | stringstream::out);
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;
297 s << setw(3) << micro;
299 s << nano / TimeSpan::nanosecondsPerTick;
303 if (!timeZoneDelta.
isNull()) {
310 s << setw(2) << timeZoneDelta.
hours() << timeZoneDelimiter << setw(2) << timeZoneDelta.
minutes();
319 string DateTime::toIsoString(
TimeSpan timeZoneDelta)
const
321 return toIsoStringWithCustomDelimiters(timeZoneDelta);
331 const char *DateTime::printDayOfWeek(
DayOfWeek dayOfWeek,
bool abbreviation)
335 case DayOfWeek::Monday:
337 case DayOfWeek::Tuesday:
339 case DayOfWeek::Wednesday:
341 case DayOfWeek::Thursday:
343 case DayOfWeek::Friday:
345 case DayOfWeek::Saturday:
347 case DayOfWeek::Sunday:
352 case DayOfWeek::Monday:
354 case DayOfWeek::Tuesday:
356 case DayOfWeek::Wednesday:
358 case DayOfWeek::Thursday:
360 case DayOfWeek::Friday:
362 case DayOfWeek::Saturday:
364 case DayOfWeek::Sunday:
371 #if defined(PLATFORM_UNIX) && !defined(PLATFORM_MAC)
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);
388 std::uint64_t DateTime::dateToTicks(
int year,
int month,
int day)
391 throw ConversionException(
"year is out of range");
394 throw ConversionException(
"month is out of range");
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");
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)
405 * TimeSpan::ticksPerDay;
411 std::uint64_t DateTime::timeToTicks(
int hour,
int minute,
int second,
double millisecond)
414 throw ConversionException(
"hour is out of range");
417 throw ConversionException(
"minute is out of range");
420 throw ConversionException(
"second is out of range");
423 throw ConversionException(
"millisecond is out of range");
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);
433 int DateTime::getDatePart(
DatePart part)
const
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;
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) {
449 if (part == DatePart::Year) {
450 return full400YearBlocks * 400 + full100YearBlocks * 100 + full4YearBlocks * 4 + full1YearBlocks + 1;
452 const auto restDays = daysMinusFull4YearBlocks - full1YearBlocks * m_daysPerYear;
453 if (part == DatePart::DayOfYear) {
456 const auto *
const daysToMonth = (full1YearBlocks == 3 && (full4YearBlocks != 24 || full100YearBlocks == 3)) ? m_daysToMonth366 : m_daysToMonth365;
458 while (restDays >= daysToMonth[month]) {
461 if (part == DatePart::Month) {
463 }
else if (part == DatePart::Day) {
464 return restDays - daysToMonth[month - 1] + 1;
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.
Represents a time interval.
constexpr bool isNull() const
Returns true if the time interval represented by the current TimeSpan class is null.
constexpr std::int64_t totalTicks() const
Returns the number of ticks that represent the value of the current TimeSpan class.
constexpr int minutes() const
Returns the minutes component of the time interval represented by the current TimeSpan class.
constexpr bool isNegative() const
Returns true if the time interval represented by the current TimeSpan class is negative.
constexpr int hours() const
Returns the hours component of the time interval represented by the current TimeSpan class.
Contains all utilities provides by the c++utilities library.
DatePart
Specifies the date part.
constexpr bool inRangeExclMax(num1 val, num2 min, num3 max)
constexpr bool inRangeInclMax(num1 val, num2 min, num3 max)
StringType argsToString(Args &&...args)
constexpr T max(T first, T second)
Returns the greatest of the given items.
DateTimeOutputFormat
Specifies the output format.
constexpr T min(T first, T second)
Returns the smallest of the given items.
DayOfWeek
Specifies the day of the week.