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 miliSecondsFact = 100.0, miliSeconds = 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 miliSeconds += (c -
'0') * miliSecondsFact;
92 miliSecondsFact /= 10;
95 *valueIndex += c -
'0';
97 }
else if ((c ==
'-' || c ==
':' || c ==
'/') || (c ==
'.' && (valueIndex == secondsIndex)) || (c ==
' ' && (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, miliSeconds);
119 std::pair<DateTime, TimeSpan> DateTime::fromIsoString(
const char *str)
121 int values[9] = { 0 };
122 int *
const yearIndex = values + 0;
123 int *
const monthIndex = values + 1;
124 int *
const dayIndex = values + 2;
125 int *
const hourIndex = values + 3;
126 int *
const secondsIndex = values + 5;
127 int *
const miliSecondsIndex = values + 6;
128 int *
const deltaHourIndex = values + 7;
129 int *valueIndex = values;
130 bool deltaNegative =
false;
131 double miliSecondsFact = 100.0, miliSeconds = 0.0;
132 for (
const char *strIndex = str;; ++strIndex) {
133 const char c = *strIndex;
134 if (c <= '9' && c >=
'0') {
135 if (valueIndex == miliSecondsIndex) {
136 miliSeconds += (c -
'0') * miliSecondsFact;
137 miliSecondsFact /= 10;
140 *valueIndex += c -
'0';
142 }
else if (c ==
'T') {
143 if (++valueIndex != hourIndex) {
146 }
else if (c ==
'-') {
147 if (valueIndex < dayIndex) {
149 }
else if (++valueIndex == deltaHourIndex) {
150 deltaNegative =
true;
154 }
else if (c ==
'.') {
155 if (valueIndex != secondsIndex) {
160 }
else if (c ==
':') {
161 if (valueIndex < hourIndex) {
163 }
else if (valueIndex == secondsIndex) {
168 }
else if ((c ==
'+') && (++valueIndex >= secondsIndex)) {
169 valueIndex = deltaHourIndex;
170 deltaNegative =
false;
171 }
else if ((c ==
'Z') && (++valueIndex >= secondsIndex)) {
172 valueIndex = deltaHourIndex + 2;
173 }
else if (c ==
'\0') {
179 auto delta(TimeSpan::fromMinutes(*deltaHourIndex * 60 + values[8]));
181 delta =
TimeSpan(-delta.totalTicks());
183 if (valueIndex < monthIndex && !*monthIndex) {
186 if (valueIndex < dayIndex && !*dayIndex) {
189 return make_pair(DateTime::fromDateAndTime(*yearIndex, *monthIndex, *dayIndex, *hourIndex, values[4], *secondsIndex, miliSeconds), delta);
199 if (format == DateTimeOutputFormat::Iso) {
200 result = toIsoString();
204 stringstream s(stringstream::in | stringstream::out);
207 if (format == DateTimeOutputFormat::IsoOmittingDefaultComponents) {
208 constexpr
auto dateDelimiter =
'-', timeDelimiter =
':';
209 const int components[] = { year(), month(), day(), hour(), minute(), second(), millisecond(), microsecond(), nanosecond() };
210 const int *
const firstTimeComponent = components + 3;
211 const int *
const firstFractionalComponent = components + 6;
212 const int *
const lastComponent = components + 8;
213 const int *componentsEnd = noMilliseconds ? firstFractionalComponent : lastComponent + 1;
214 for (
const int *
i = componentsEnd - 1;
i > components; --
i) {
215 if (
i >= firstTimeComponent && *
i == 0) {
217 }
else if (
i < firstTimeComponent && *
i == 1) {
221 for (
const int *
i = components;
i != componentsEnd; ++
i) {
222 if (
i == firstTimeComponent) {
224 }
else if (
i == firstFractionalComponent) {
227 if (
i == components) {
229 }
else if (
i < firstFractionalComponent) {
230 if (
i < firstTimeComponent) {
232 }
else if (
i > firstTimeComponent) {
236 }
else if (
i < lastComponent) {
239 s << *
i / TimeSpan::nanosecondsPerTick;
246 if (format == DateTimeOutputFormat::DateTimeAndWeekday || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
247 s << printDayOfWeek(dayOfWeek(), format == DateTimeOutputFormat::DateTimeAndShortWeekday) <<
' ';
248 if (format == DateTimeOutputFormat::DateOnly || format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday
249 || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
250 s << setw(4) << year() <<
'-' << setw(2) << month() <<
'-' << setw(2) << day();
251 if (format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday
252 || format == DateTimeOutputFormat::DateTimeAndShortWeekday)
254 if (format == DateTimeOutputFormat::TimeOnly || format == DateTimeOutputFormat::DateAndTime || format == DateTimeOutputFormat::DateTimeAndWeekday
255 || format == DateTimeOutputFormat::DateTimeAndShortWeekday) {
256 s << setw(2) << hour() <<
':' << setw(2) << minute() <<
':' << setw(2) << second();
257 int ms = millisecond();
258 if (!noMilliseconds && ms > 0) {
259 s <<
'.' << setw(3) << ms;
269 string DateTime::toIsoStringWithCustomDelimiters(
TimeSpan timeZoneDelta,
char dateDelimiter,
char timeDelimiter,
char timeZoneDelimiter)
const
271 stringstream s(stringstream::in | stringstream::out);
273 s << setw(4) << year() << dateDelimiter << setw(2) << month() << dateDelimiter << setw(2) << day() <<
'T' << setw(2) << hour() << timeDelimiter
274 << setw(2) << minute() << timeDelimiter << setw(2) << second();
275 const int milli(millisecond());
276 const int micro(microsecond());
277 const int nano(nanosecond());
278 if (milli || micro || nano) {
279 s <<
'.' << setw(3) << milli;
281 s << setw(3) << micro;
283 s << nano / TimeSpan::nanosecondsPerTick;
287 if (!timeZoneDelta.
isNull()) {
294 s << setw(2) << timeZoneDelta.
hours() << timeZoneDelimiter << setw(2) << timeZoneDelta.
minutes();
303 string DateTime::toIsoString(
TimeSpan timeZoneDelta)
const
305 return toIsoStringWithCustomDelimiters(timeZoneDelta);
315 const char *DateTime::printDayOfWeek(
DayOfWeek dayOfWeek,
bool abbreviation)
319 case DayOfWeek::Monday:
321 case DayOfWeek::Tuesday:
323 case DayOfWeek::Wednesday:
325 case DayOfWeek::Thursday:
327 case DayOfWeek::Friday:
329 case DayOfWeek::Saturday:
331 case DayOfWeek::Sunday:
336 case DayOfWeek::Monday:
338 case DayOfWeek::Tuesday:
340 case DayOfWeek::Wednesday:
342 case DayOfWeek::Thursday:
344 case DayOfWeek::Friday:
346 case DayOfWeek::Saturday:
348 case DayOfWeek::Sunday:
355 #if defined(PLATFORM_UNIX) && !defined(PLATFORM_MAC)
363 clock_gettime(CLOCK_REALTIME, &t);
364 return DateTime(DateTime::unixEpochStart().totalTicks() +
static_cast<std::uint64_t
>(t.tv_sec) * TimeSpan::ticksPerSecond
365 +
static_cast<std::uint64_t
>(t.tv_nsec) / 100);
372 std::uint64_t DateTime::dateToTicks(
int year,
int month,
int day)
375 throw ConversionException(
"year is out of range");
378 throw ConversionException(
"month is out of range");
380 const auto *
const daysToMonth =
reinterpret_cast<const int *
>(isLeapYear(year) ? m_daysToMonth366 : m_daysToMonth365);
381 const int passedMonth = month - 1;
382 if (!
inRangeInclMax(day, 1, daysToMonth[month] - daysToMonth[passedMonth])) {
383 throw ConversionException(
"day is out of range");
385 const auto passedYears =
static_cast<unsigned int>(year - 1);
386 const auto passedDays =
static_cast<unsigned int>(day - 1);
387 return (passedYears * m_daysPerYear + passedYears / 4 - passedYears / 100 + passedYears / 400
388 +
static_cast<unsigned int>(daysToMonth[passedMonth]) + passedDays)
389 * TimeSpan::ticksPerDay;
395 std::uint64_t DateTime::timeToTicks(
int hour,
int minute,
int second,
double millisecond)
398 throw ConversionException(
"hour is out of range");
401 throw ConversionException(
"minute is out of range");
404 throw ConversionException(
"second is out of range");
407 throw ConversionException(
"millisecond is out of range");
409 return static_cast<std::uint64_t
>(hour * TimeSpan::ticksPerHour) +
static_cast<std::uint64_t
>(minute * TimeSpan::ticksPerMinute)
410 +
static_cast<std::uint64_t
>(second * TimeSpan::ticksPerSecond) +
static_cast<std::uint64_t
>(millisecond * TimeSpan::ticksPerMillisecond);
417 int DateTime::getDatePart(
DatePart part)
const
419 const auto fullDays =
static_cast<int>(m_ticks / TimeSpan::ticksPerDay);
420 const auto full400YearBlocks = fullDays / m_daysPer400Years;
421 const auto daysMinusFull400YearBlocks = fullDays - full400YearBlocks * m_daysPer400Years;
422 auto full100YearBlocks = daysMinusFull400YearBlocks / m_daysPer100Years;
423 if (full100YearBlocks == 4) {
424 full100YearBlocks = 3;
426 const auto daysMinusFull100YearBlocks = daysMinusFull400YearBlocks - full100YearBlocks * m_daysPer100Years;
427 const auto full4YearBlocks = daysMinusFull100YearBlocks / m_daysPer4Years;
428 const auto daysMinusFull4YearBlocks = daysMinusFull100YearBlocks - full4YearBlocks * m_daysPer4Years;
429 auto full1YearBlocks = daysMinusFull4YearBlocks / m_daysPerYear;
430 if (full1YearBlocks == 4) {
433 if (part == DatePart::Year) {
434 return full400YearBlocks * 400 + full100YearBlocks * 100 + full4YearBlocks * 4 + full1YearBlocks + 1;
436 const auto restDays = daysMinusFull4YearBlocks - full1YearBlocks * m_daysPerYear;
437 if (part == DatePart::DayOfYear) {
440 const auto *
const daysToMonth = (full1YearBlocks == 3 && (full4YearBlocks != 24 || full100YearBlocks == 3)) ? m_daysToMonth366 : m_daysToMonth365;
442 while (restDays >= daysToMonth[month]) {
445 if (part == DatePart::Month) {
447 }
else if (part == DatePart::Day) {
448 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 ture 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 ture 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.