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 int fullDays = m_ticks / TimeSpan::ticksPerDay;
420 const int full400YearBlocks = fullDays / m_daysPer400Years;
421 const int daysMinusFull400YearBlocks = fullDays - full400YearBlocks * m_daysPer400Years;
422 int full100YearBlocks = daysMinusFull400YearBlocks / m_daysPer100Years;
423 if (full100YearBlocks == 4) {
424 full100YearBlocks = 3;
426 const int daysMinusFull100YearBlocks = daysMinusFull400YearBlocks - full100YearBlocks * m_daysPer100Years;
427 const int full4YearBlocks = daysMinusFull100YearBlocks / m_daysPer4Years;
428 const int daysMinusFull4YearBlocks = daysMinusFull100YearBlocks - full4YearBlocks * m_daysPer4Years;
429 int 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 int restDays = daysMinusFull4YearBlocks - full1YearBlocks * m_daysPerYear;
437 if (part == DatePart::DayOfYear) {
440 const int *
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;