Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add ability to emit the NT epoch.
  • Loading branch information
BillyONeal committed Jan 26, 2021
commit 7046d3a996f0ce55aa9dc041b020012302d80bc4
84 changes: 47 additions & 37 deletions Release/src/utilities/asyncrt_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ scoped_c_thread_locale::~scoped_c_thread_locale()
}
}
#elif (defined(ANDROID) || defined(__ANDROID__))
scoped_c_thread_locale::scoped_c_thread_locale() {}
scoped_c_thread_locale::~scoped_c_thread_locale() {}
scoped_c_thread_locale::scoped_c_thread_locale() { }
scoped_c_thread_locale::~scoped_c_thread_locale() { }
#else
scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(nullptr)
{
Expand Down Expand Up @@ -620,7 +620,13 @@ utf16string __cdecl conversions::to_utf16string(const std::string& value) { retu

static const int64_t NtToUnixOffsetSeconds = 11644473600; // diff between windows and unix epochs (seconds)

static bool year_is_leap_year(int year) { return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); }
static bool year_is_leap_year_1601(int year)
{
int decimal_year = year + 1601;
return (decimal_year % 4 == 0 && (decimal_year % 100 != 0 || decimal_year % 400 == 0));
}

static bool year_is_leap_year(int year) { return year_is_leap_year_1601(year + 299); }

static const int SecondsInMinute = 60;
static const int SecondsInHour = SecondsInMinute * 60;
Expand All @@ -639,26 +645,27 @@ static const int64_t SecondsFrom1900To2001 = INT64_C(3187296000);

static const int64_t NtTo1900OffsetInterval = INT64_C(0x014F373BFDE04000);

static int count_leap_years(const int yearsSince1900)
static int count_leap_years_1601(int yearsSince1601)
{
int tmpYears = yearsSince1900 + 299; // shift into 1601, the first 400 year cycle including 1900

int year400 = tmpYears / 400;
tmpYears -= year400 * 400;
int year400 = yearsSince1601 / 400;
yearsSince1601 -= year400 * 400;
int result = year400 * 97;

int year100 = tmpYears / 100;
tmpYears -= year100 * 100;
int year100 = yearsSince1601 / 100;
yearsSince1601 -= year100 * 100;
result += year100 * 24;

result += tmpYears / 4;

// subtract off leap years from 1601
result -= 72;
result += yearsSince1601 / 4;

return result;
}

static int count_leap_years(const int yearsSince1900)
{
// shift into 1601, the first 400 year cycle including 1900, then subtract leap years from 1601->1900
return count_leap_years_1601(yearsSince1900 + 299) - 72;
}

// The following table assumes no leap year; leap year is added separately
static const unsigned short cumulative_days_to_month[12] = {
0, // Jan
Expand Down Expand Up @@ -720,20 +727,16 @@ struct compute_year_result
int secondsLeftThisYear;
};

static const int64_t secondsFrom1601To1900 = INT64_C(9435484800);

static compute_year_result compute_year(int64_t secondsSince1900)
static compute_year_result compute_year_1601(int64_t secondsSince1601)
{
int64_t secondsLeft = secondsSince1900 + secondsFrom1601To1900; // shift to start of this 400 year cycle

int year400 = static_cast<int>(secondsLeft / SecondsIn400Years);
secondsLeft -= year400 * SecondsIn400Years;
int year400 = static_cast<int>(secondsSince1601 / SecondsIn400Years);
secondsSince1601 -= year400 * SecondsIn400Years;

int year100 = static_cast<int>(secondsLeft / SecondsIn100Years);
secondsLeft -= year100 * SecondsIn100Years;
int year100 = static_cast<int>(secondsSince1601 / SecondsIn100Years);
secondsSince1601 -= year100 * SecondsIn100Years;

int year4 = static_cast<int>(secondsLeft / SecondsIn4Years);
int secondsInt = static_cast<int>(secondsLeft - year4 * SecondsIn4Years);
int year4 = static_cast<int>(secondsSince1601 / SecondsIn4Years);
int secondsInt = static_cast<int>(secondsSince1601 - year4 * SecondsIn4Years);

int year1 = secondsInt / SecondsInYear;
if (year1 == 4)
Expand All @@ -743,9 +746,16 @@ static compute_year_result compute_year(int64_t secondsSince1900)
}

secondsInt -= year1 * SecondsInYear;
return {year400 * 400 + year100 * 100 + year4 * 4 + year1, secondsInt};
}

// shift back to 1900 base from 1601:
return {year400 * 400 + year100 * 100 + year4 * 4 + year1 - 299, secondsInt};
static const int64_t secondsFrom1601To1900 = INT64_C(9435484800);
static compute_year_result compute_year(int64_t secondsSince1900)
{
// shift to start of this 400 year cycle
auto partialResult = compute_year_1601(secondsSince1900 + secondsFrom1601To1900);
// shift back to 1900
return {partialResult.year - 299, partialResult.secondsLeftThisYear};
}

utility::string_t datetime::to_string(date_format format) const
Expand All @@ -755,11 +765,11 @@ utility::string_t datetime::to_string(date_format format) const
throw std::out_of_range("The requested year exceeds the year 9999.");
}

const int64_t epochAdjusted = static_cast<int64_t>(m_interval) - NtTo1900OffsetInterval;
const int64_t secondsSince1900 = epochAdjusted / _secondTicks; // convert to seconds
const int fracSec = static_cast<int>(epochAdjusted % _secondTicks);
const int64_t interval = static_cast<int64_t>(m_interval);
const int64_t secondsSince1601 = interval / _secondTicks; // convert to seconds
const int fracSec = static_cast<int>(interval % _secondTicks);

const auto yearData = compute_year(secondsSince1900);
const auto yearData = compute_year_1601(secondsSince1601);
const int year = yearData.year;
const int yearDay = yearData.secondsLeftThisYear / SecondsInDay;
int leftover = yearData.secondsLeftThisYear % SecondsInDay;
Expand All @@ -768,15 +778,15 @@ utility::string_t datetime::to_string(date_format format) const
const int minute = leftover / SecondsInMinute;
leftover = leftover % SecondsInMinute;

const auto& monthTable = year_is_leap_year(year) ? cumulative_days_to_month_leap : cumulative_days_to_month;
const auto& monthTable = year_is_leap_year_1601(year) ? cumulative_days_to_month_leap : cumulative_days_to_month;
int month = 0;
while (month < 11 && monthTable[month + 1] <= yearDay)
{
++month;
}

const auto monthDay = yearDay - monthTable[month] + 1;
const auto weekday = static_cast<int>((secondsSince1900 / SecondsInDay + 1) % 7);
const auto weekday = static_cast<int>((secondsSince1601 / SecondsInDay + 1) % 7);

char outBuffer[38]; // Thu, 01 Jan 1970 00:00:00 GMT\0
// 1970-01-01T00:00:00.1234567Z\0
Expand All @@ -791,7 +801,7 @@ utility::string_t datetime::to_string(date_format format) const
dayNames + 4 * weekday,
monthDay,
monthNames + 4 * month,
year + 1900,
year + 1601,
hour,
minute,
leftover);
Expand All @@ -801,7 +811,7 @@ utility::string_t datetime::to_string(date_format format) const
dayNames + 4 * weekday,
monthDay,
monthNames + 4 * month,
year + 1900,
year + 1601,
hour,
minute,
leftover);
Expand All @@ -815,15 +825,15 @@ utility::string_t datetime::to_string(date_format format) const
sprintf_s(outCursor,
20,
"%04d-%02d-%02dT%02d:%02d:%02d",
year + 1900,
year + 1601,
month + 1,
monthDay,
hour,
minute,
leftover);
#else // ^^^ _MSC_VER // !_MSC_VER vvv
sprintf(
outCursor, "%04d-%02d-%02dT%02d:%02d:%02d", year + 1900, month + 1, monthDay, hour, minute, leftover);
outCursor, "%04d-%02d-%02dT%02d:%02d:%02d", year + 1601, month + 1, monthDay, hour, minute, leftover);
#endif // _MSC_VER
outCursor += 19;
if (fracSec != 0)
Expand Down
14 changes: 14 additions & 0 deletions Release/tests/functional/utils/datetime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,20 @@ SUITE(datetime)
}
}

TEST(can_emit_nt_epoch_zero)
{
// ISO 8601
{
auto result = utility::datetime{}.to_string(utility::datetime::RFC_1123);
VERIFY_ARE_EQUAL(_XPLATSTR("Mon, 01 Jan 1601 00:00:00 GMT"), result);
}
// ISO 8601
{
auto result = utility::datetime{}.to_string(utility::datetime::ISO_8601);
VERIFY_ARE_EQUAL(_XPLATSTR("1601-01-01T00:00:00Z"), result);
}
}

} // SUITE(datetime)

} // namespace utils_tests
Expand Down