Skip to content

Commit 7046d3a

Browse files
committed
Add ability to emit the NT epoch.
1 parent 880e535 commit 7046d3a

File tree

2 files changed

+61
-37
lines changed

2 files changed

+61
-37
lines changed

Release/src/utilities/asyncrt_utils.cpp

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,8 @@ scoped_c_thread_locale::~scoped_c_thread_locale()
177177
}
178178
}
179179
#elif (defined(ANDROID) || defined(__ANDROID__))
180-
scoped_c_thread_locale::scoped_c_thread_locale() {}
181-
scoped_c_thread_locale::~scoped_c_thread_locale() {}
180+
scoped_c_thread_locale::scoped_c_thread_locale() { }
181+
scoped_c_thread_locale::~scoped_c_thread_locale() { }
182182
#else
183183
scoped_c_thread_locale::scoped_c_thread_locale() : m_prevLocale(nullptr)
184184
{
@@ -620,7 +620,13 @@ utf16string __cdecl conversions::to_utf16string(const std::string& value) { retu
620620

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

623-
static bool year_is_leap_year(int year) { return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); }
623+
static bool year_is_leap_year_1601(int year)
624+
{
625+
int decimal_year = year + 1601;
626+
return (decimal_year % 4 == 0 && (decimal_year % 100 != 0 || decimal_year % 400 == 0));
627+
}
628+
629+
static bool year_is_leap_year(int year) { return year_is_leap_year_1601(year + 299); }
624630

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

640646
static const int64_t NtTo1900OffsetInterval = INT64_C(0x014F373BFDE04000);
641647

642-
static int count_leap_years(const int yearsSince1900)
648+
static int count_leap_years_1601(int yearsSince1601)
643649
{
644-
int tmpYears = yearsSince1900 + 299; // shift into 1601, the first 400 year cycle including 1900
645-
646-
int year400 = tmpYears / 400;
647-
tmpYears -= year400 * 400;
650+
int year400 = yearsSince1601 / 400;
651+
yearsSince1601 -= year400 * 400;
648652
int result = year400 * 97;
649653

650-
int year100 = tmpYears / 100;
651-
tmpYears -= year100 * 100;
654+
int year100 = yearsSince1601 / 100;
655+
yearsSince1601 -= year100 * 100;
652656
result += year100 * 24;
653657

654-
result += tmpYears / 4;
655-
656-
// subtract off leap years from 1601
657-
result -= 72;
658+
result += yearsSince1601 / 4;
658659

659660
return result;
660661
}
661662

663+
static int count_leap_years(const int yearsSince1900)
664+
{
665+
// shift into 1601, the first 400 year cycle including 1900, then subtract leap years from 1601->1900
666+
return count_leap_years_1601(yearsSince1900 + 299) - 72;
667+
}
668+
662669
// The following table assumes no leap year; leap year is added separately
663670
static const unsigned short cumulative_days_to_month[12] = {
664671
0, // Jan
@@ -720,20 +727,16 @@ struct compute_year_result
720727
int secondsLeftThisYear;
721728
};
722729

723-
static const int64_t secondsFrom1601To1900 = INT64_C(9435484800);
724-
725-
static compute_year_result compute_year(int64_t secondsSince1900)
730+
static compute_year_result compute_year_1601(int64_t secondsSince1601)
726731
{
727-
int64_t secondsLeft = secondsSince1900 + secondsFrom1601To1900; // shift to start of this 400 year cycle
728-
729-
int year400 = static_cast<int>(secondsLeft / SecondsIn400Years);
730-
secondsLeft -= year400 * SecondsIn400Years;
732+
int year400 = static_cast<int>(secondsSince1601 / SecondsIn400Years);
733+
secondsSince1601 -= year400 * SecondsIn400Years;
731734

732-
int year100 = static_cast<int>(secondsLeft / SecondsIn100Years);
733-
secondsLeft -= year100 * SecondsIn100Years;
735+
int year100 = static_cast<int>(secondsSince1601 / SecondsIn100Years);
736+
secondsSince1601 -= year100 * SecondsIn100Years;
734737

735-
int year4 = static_cast<int>(secondsLeft / SecondsIn4Years);
736-
int secondsInt = static_cast<int>(secondsLeft - year4 * SecondsIn4Years);
738+
int year4 = static_cast<int>(secondsSince1601 / SecondsIn4Years);
739+
int secondsInt = static_cast<int>(secondsSince1601 - year4 * SecondsIn4Years);
737740

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

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

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

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

758-
const int64_t epochAdjusted = static_cast<int64_t>(m_interval) - NtTo1900OffsetInterval;
759-
const int64_t secondsSince1900 = epochAdjusted / _secondTicks; // convert to seconds
760-
const int fracSec = static_cast<int>(epochAdjusted % _secondTicks);
768+
const int64_t interval = static_cast<int64_t>(m_interval);
769+
const int64_t secondsSince1601 = interval / _secondTicks; // convert to seconds
770+
const int fracSec = static_cast<int>(interval % _secondTicks);
761771

762-
const auto yearData = compute_year(secondsSince1900);
772+
const auto yearData = compute_year_1601(secondsSince1601);
763773
const int year = yearData.year;
764774
const int yearDay = yearData.secondsLeftThisYear / SecondsInDay;
765775
int leftover = yearData.secondsLeftThisYear % SecondsInDay;
@@ -768,15 +778,15 @@ utility::string_t datetime::to_string(date_format format) const
768778
const int minute = leftover / SecondsInMinute;
769779
leftover = leftover % SecondsInMinute;
770780

771-
const auto& monthTable = year_is_leap_year(year) ? cumulative_days_to_month_leap : cumulative_days_to_month;
781+
const auto& monthTable = year_is_leap_year_1601(year) ? cumulative_days_to_month_leap : cumulative_days_to_month;
772782
int month = 0;
773783
while (month < 11 && monthTable[month + 1] <= yearDay)
774784
{
775785
++month;
776786
}
777787

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

781791
char outBuffer[38]; // Thu, 01 Jan 1970 00:00:00 GMT\0
782792
// 1970-01-01T00:00:00.1234567Z\0
@@ -791,7 +801,7 @@ utility::string_t datetime::to_string(date_format format) const
791801
dayNames + 4 * weekday,
792802
monthDay,
793803
monthNames + 4 * month,
794-
year + 1900,
804+
year + 1601,
795805
hour,
796806
minute,
797807
leftover);
@@ -801,7 +811,7 @@ utility::string_t datetime::to_string(date_format format) const
801811
dayNames + 4 * weekday,
802812
monthDay,
803813
monthNames + 4 * month,
804-
year + 1900,
814+
year + 1601,
805815
hour,
806816
minute,
807817
leftover);
@@ -815,15 +825,15 @@ utility::string_t datetime::to_string(date_format format) const
815825
sprintf_s(outCursor,
816826
20,
817827
"%04d-%02d-%02dT%02d:%02d:%02d",
818-
year + 1900,
828+
year + 1601,
819829
month + 1,
820830
monthDay,
821831
hour,
822832
minute,
823833
leftover);
824834
#else // ^^^ _MSC_VER // !_MSC_VER vvv
825835
sprintf(
826-
outCursor, "%04d-%02d-%02dT%02d:%02d:%02d", year + 1900, month + 1, monthDay, hour, minute, leftover);
836+
outCursor, "%04d-%02d-%02dT%02d:%02d:%02d", year + 1601, month + 1, monthDay, hour, minute, leftover);
827837
#endif // _MSC_VER
828838
outCursor += 19;
829839
if (fracSec != 0)

Release/tests/functional/utils/datetime.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,20 @@ SUITE(datetime)
502502
}
503503
}
504504

505+
TEST(can_emit_nt_epoch_zero)
506+
{
507+
// ISO 8601
508+
{
509+
auto result = utility::datetime{}.to_string(utility::datetime::RFC_1123);
510+
VERIFY_ARE_EQUAL(_XPLATSTR("Mon, 01 Jan 1601 00:00:00 GMT"), result);
511+
}
512+
// ISO 8601
513+
{
514+
auto result = utility::datetime{}.to_string(utility::datetime::ISO_8601);
515+
VERIFY_ARE_EQUAL(_XPLATSTR("1601-01-01T00:00:00Z"), result);
516+
}
517+
}
518+
505519
} // SUITE(datetime)
506520

507521
} // namespace utils_tests

0 commit comments

Comments
 (0)