diff --git a/lib/Runtime/PlatformAgnostic/Platform/CMakeLists.txt b/lib/Runtime/PlatformAgnostic/Platform/CMakeLists.txt index d294d3d30a8..60f41c9399d 100644 --- a/lib/Runtime/PlatformAgnostic/Platform/CMakeLists.txt +++ b/lib/Runtime/PlatformAgnostic/Platform/CMakeLists.txt @@ -3,6 +3,7 @@ project(CHAKRA_PLATFORM_AGNOSTIC) set(PL_SOURCE_FILES Common/UnicodeText.Common.cpp Common/HiResTimer.cpp + Common/DateTime.cpp Linux/EventTrace.cpp Linux/UnicodeText.ICU.cpp Linux/NumbersUtility.cpp @@ -14,13 +15,11 @@ set(PL_SOURCE_FILES if(CC_TARGET_OS_ANDROID OR CC_TARGET_OS_LINUX) set(PL_SOURCE_FILES ${PL_SOURCE_FILES} - Linux/DateTime.cpp Linux/SystemInfo.cpp ) elseif(CC_TARGET_OS_OSX) set(PL_SOURCE_FILES ${PL_SOURCE_FILES} Unix/AssemblyCommon.cpp - Unix/DateTime.cpp Unix/SystemInfo.cpp ) endif() diff --git a/lib/Runtime/PlatformAgnostic/Platform/Unix/DateTime.cpp b/lib/Runtime/PlatformAgnostic/Platform/Common/DateTime.cpp similarity index 60% rename from lib/Runtime/PlatformAgnostic/Platform/Unix/DateTime.cpp rename to lib/Runtime/PlatformAgnostic/Platform/Common/DateTime.cpp index d9da27dbef4..57ed7882cfc 100644 --- a/lib/Runtime/PlatformAgnostic/Platform/Unix/DateTime.cpp +++ b/lib/Runtime/PlatformAgnostic/Platform/Common/DateTime.cpp @@ -3,39 +3,68 @@ // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- -#ifndef __APPLE__ -// todo: for BSD consider moving this file into macOS folder -#include "../Linux/DateTime.cpp" -#else #include "Common.h" #include "ChakraPlatform.h" -#include -#include namespace PlatformAgnostic { namespace DateTime { - const WCHAR *Utility::GetStandardName(size_t *nameLength, const DateTime::YMD *ymd) + int GetTZ(double tv, WCHAR* dst_name, bool* is_dst, int* offset) { - AssertMsg(ymd != NULL, "xplat needs DateTime::YMD is defined for this call"); - double tv = Js::DateUtilities::TvFromDate(ymd->year, ymd->mon, ymd->mday, ymd->time); - int64_t absoluteTime = tv / 1000; - absoluteTime -= kCFAbsoluteTimeIntervalSince1970; + struct tm tm_local, *tm_result; + time_t time_noms = (time_t) (tv / 1000 /* drop ms */); + tm_result = localtime_r(&time_noms, &tm_local); + if (!tm_result) + { + *is_dst = false; + *offset = 0; + if (dst_name != nullptr) + { + dst_name[0] = (WCHAR) 0; + } + return 0; + } + + *is_dst = tm_result->tm_isdst > 0; + *offset = (int) tm_result->tm_gmtoff; - CFTimeZoneRef timeZone = CFTimeZoneCopySystem(); + if (dst_name != nullptr) + { + if (!tm_result->tm_zone) + { + dst_name[0] = 0; + return 0; + } - int offset = (int)CFTimeZoneGetSecondsFromGMT(timeZone, (CFAbsoluteTime)absoluteTime); - absoluteTime -= offset; + uint32 length = 0; + for (; length < __CC_PA_TIMEZONE_ABVR_NAME_LENGTH + && tm_result->tm_zone[length] != 0; length++) + { + dst_name[length] = (WCHAR) tm_result->tm_zone[length]; + } - char tz_name[128]; - CFStringRef abbr = CFTimeZoneCopyAbbreviation(timeZone, absoluteTime); - CFRelease(timeZone); - CFStringGetCString(abbr, tz_name, sizeof(tz_name), kCFStringEncodingUTF16); - wcscpy_s(data.standardName, 32, reinterpret_cast(tz_name)); - data.standardNameLength = CFStringGetLength(abbr); - CFRelease(abbr); + if (length >= __CC_PA_TIMEZONE_ABVR_NAME_LENGTH) + { + length = __CC_PA_TIMEZONE_ABVR_NAME_LENGTH - 1; + } + dst_name[length] = (WCHAR)0; + return length; + } + else + { + return 0; + } + } + + const WCHAR *Utility::GetStandardName(size_t *nameLength, const DateTime::YMD *ymd) + { + AssertMsg(ymd != NULL, "xplat needs DateTime::YMD is defined for this call"); + double tv = Js::DateUtilities::TvFromDate(ymd->year, ymd->mon, ymd->mday, ymd->time); + bool isDST; + int mOffset; + data.standardNameLength = GetTZ(tv, data.standardName, &isDST, &mOffset); *nameLength = data.standardNameLength; return data.standardName; } @@ -46,22 +75,11 @@ namespace DateTime return GetStandardName(nameLength, ymd); } - static time_t IsDST(double tv, int *offset) - { - CFTimeZoneRef timeZone = CFTimeZoneCopySystem(); - int64_t absoluteTime = tv / 1000; - absoluteTime -= kCFAbsoluteTimeIntervalSince1970; - *offset = (int)CFTimeZoneGetSecondsFromGMT(timeZone, (CFAbsoluteTime)absoluteTime); - - time_t result = CFTimeZoneIsDaylightSavingTime(timeZone, (CFAbsoluteTime)absoluteTime); - CFRelease(timeZone); - return result; - } - static void YMDLocalToUtc(double localtv, YMD *utc) { int mOffset = 0; - bool isDST = IsDST(localtv, &mOffset); + bool isDST; + GetTZ(localtv, nullptr, &isDST, &mOffset); localtv -= DateTimeTicks_PerSecond * mOffset; Js::DateUtilities::GetYmdFromTv(localtv, utc); } @@ -70,7 +88,8 @@ namespace DateTime int &bias, int &offset, bool &isDaylightSavings) { int mOffset = 0; - bool isDST = IsDST(utctv, &mOffset); + bool isDST; + GetTZ(utctv, nullptr, &isDST, &mOffset); utctv += DateTimeTicks_PerSecond * mOffset; Js::DateUtilities::GetYmdFromTv(utctv, local); isDaylightSavings = isDST; @@ -97,4 +116,3 @@ namespace DateTime } } // namespace DateTime } // namespace PlatformAgnostic -#endif diff --git a/lib/Runtime/PlatformAgnostic/Platform/Linux/DateTime.cpp b/lib/Runtime/PlatformAgnostic/Platform/Linux/DateTime.cpp deleted file mode 100644 index 54574eb59d8..00000000000 --- a/lib/Runtime/PlatformAgnostic/Platform/Linux/DateTime.cpp +++ /dev/null @@ -1,229 +0,0 @@ -//------------------------------------------------------------------------------------------------------- -// Copyright (C) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. -//------------------------------------------------------------------------------------------------------- - -#include "Common.h" -#include -#include -#include "ChakraPlatform.h" - -namespace PlatformAgnostic -{ -namespace DateTime -{ - static inline bool IsLeap(const int year) - { - return (0 == (year & 3)) && (0 != (year % 100) || 0 == (year % 400)); - } - - // Windows DateTime implementation normalizes the year beyond <1900 >2100 - // mktime etc. broken-out time bases 1900 - static inline int NormalizeYMDYear(const int base_year) - { - int retval = base_year; - - if (base_year < -2100) - { - retval = 2100; - } - else if (base_year < -1900) - { - retval = base_year * -1; - } - else if (base_year >= 0 && base_year < 100) - { - retval = 1900 + base_year; - } - else if (base_year < 0) - { - retval = NormalizeYMDYear(-1 * base_year); - } - - return retval; - } - - static inline int UpdateToYMDYear(const int base_year, const struct tm *time) - { - int year = time->tm_year; - - if (base_year < -2100) - { - const int diff = year - 2100; - year = abs(base_year) - diff; - } - - if (base_year < -1900) - { - year *= -1; - } - else if (base_year >= 0 && base_year < 100) - { - year -= 1900; - } - else if (base_year < 0) - { - const int org_base_year = NormalizeYMDYear(-1 * base_year); - year = base_year - (org_base_year - year); - } - - return year; - } - - static void YMD_TO_TM(const YMD *ymd, struct tm *time, bool *leap_added) - { - time->tm_year = NormalizeYMDYear(ymd->year); - time->tm_mon = ymd->mon; - time->tm_wday = ymd->wday; - time->tm_mday = ymd->mday; - int t = ymd->time; - t /= DateTimeTicks_PerSecond; // discard ms - time->tm_sec = t % 60; - t /= 60; - time->tm_min = t % 60; - t /= 60; - time->tm_hour = t; - - // mktime etc. broken-out time accepts 1900 as a start year while epoch is 1970 - // temporarily add a calendar day for leap pass - bool leap_year = IsLeap(time->tm_year); - *leap_added = false; - if (ymd->yday == 60 && leap_year) { - time->tm_mday++; - *leap_added = true; - } - } - - static void TM_TO_YMD(const struct tm *time, YMD *ymd, const bool leap_added, const int base_year) - { - ymd->year = UpdateToYMDYear(base_year, time); - ymd->mon = time->tm_mon; - ymd->mday = time->tm_mday; - - ymd->time = (time->tm_hour * DateTimeTicks_PerHour) - + (time->tm_min * DateTimeTicks_PerMinute) - + (time->tm_sec * DateTimeTicks_PerSecond); - - // mktime etc. broken-out time accepts 1900 as a start year while epoch is 1970 - // minus the previously added calendar day (see YMD_TO_TM) - if (leap_added) - { - AssertMsg(ymd->mday >= 0, "Day of month can't be a negative number"); - if (ymd->mday == 0) - { - ymd->mday = 29; - ymd->mon = 1; - } - else - { - ymd->mday--; - } - } - } - - static void CopyTimeZoneName(WCHAR *wstr, size_t *length, const char *tm_zone) - { - *length = strlen(tm_zone); - - for(int i = 0; i < *length; i++) - { - wstr[i] = (WCHAR)tm_zone[i]; - } - - wstr[*length] = (WCHAR)0; - } - - const WCHAR *Utility::GetStandardName(size_t *nameLength, const DateTime::YMD *ymd) - { - AssertMsg(ymd != NULL, "xplat needs DateTime::YMD is defined for this call"); - struct tm time_tm = {0}; - bool leap_added; - YMD_TO_TM(ymd, &time_tm, &leap_added); - timelocal(&time_tm); // get zone name for the given date - CopyTimeZoneName(data.standardName, &data.standardNameLength, time_tm.tm_zone); - *nameLength = data.standardNameLength; - return data.standardName; - } - - const WCHAR *Utility::GetDaylightName(size_t *nameLength, const DateTime::YMD *ymd) - { - // xplat only gets the actual zone name for the given date - return GetStandardName(nameLength, ymd); - } - - static void YMDLocalToUtc(YMD *local, YMD *utc) - { - struct tm local_tm = {0}; - bool leap_added; - YMD_TO_TM(local, (&local_tm), &leap_added); - - // tm doesn't have milliseconds - int milliseconds = local->time % 1000; - - tzset(); - time_t utime = timelocal(&local_tm); - if (local_tm.tm_isdst) - { - utime -= 3600; - } - - struct tm utc_tm = {0}; - if (gmtime_r(&utime, &utc_tm) == 0) - { - AssertMsg(false, "gmtime() failed"); - } - - TM_TO_YMD((&utc_tm), utc, leap_added, local->year); - // put milliseconds back - utc->time += milliseconds; - } - - static void YMDUtcToLocal(YMD *utc, YMD *local, - int &bias, int &offset, bool &isDaylightSavings) - { - struct tm utc_tm = {0}; - bool leap_added; - YMD_TO_TM(utc, &utc_tm, &leap_added); - - // tm doesn't have milliseconds - int milliseconds = utc->time % 1000; - - tzset(); - time_t ltime = timegm(&utc_tm); - - struct tm local_tm = {0}; - localtime_r(<ime, &local_tm); - - TM_TO_YMD((&local_tm), local, leap_added, utc->year); - // put milliseconds back - local->time += milliseconds; - - isDaylightSavings = local_tm.tm_isdst ? false : true; - offset = (local_tm.tm_gmtoff / 60); - bias = offset; - } - - // DaylightTimeHelper ****** - double DaylightTimeHelper::UtcToLocal(double utcTime, int &bias, - int &offset, bool &isDaylightSavings) - { - YMD ymdUTC, local; - - Js::DateUtilities::GetYmdFromTv(utcTime, &ymdUTC); - YMDUtcToLocal(&ymdUTC, &local, - bias, offset, isDaylightSavings); - - return Js::DateUtilities::TvFromDate(local.year, local.mon, local.mday, local.time); - } - - double DaylightTimeHelper::LocalToUtc(double localTime) - { - YMD ymdLocal, utc; - - Js::DateUtilities::GetYmdFromTv(localTime, &ymdLocal); - YMDLocalToUtc(&ymdLocal, &utc); - - return Js::DateUtilities::TvFromDate(utc.year, utc.mon, utc.mday, utc.time); - } -} // namespace DateTime -} // namespace PlatformAgnostic diff --git a/test/Date/DateCtr.baseline b/test/Date/DateCtr.baseline deleted file mode 100644 index 6c4847c6e7b..00000000000 --- a/test/Date/DateCtr.baseline +++ /dev/null @@ -1,15 +0,0 @@ -1970-01-10T00:00:01.000Z -1974-01-01T00:00:00.000Z -1970-01-01T00:00:01.974Z -1974-10-01T07:00:00.000Z -1974-10-24T07:00:00.000Z -1974-10-24T07:00:00.000Z -1974-10-24T07:20:00.000Z -1974-10-24T07:20:30.000Z -1974-10-24T07:20:30.040Z -1974-10-24T07:20:30.040Z --098001-12-01T08:00:00.000Z -1999-12-01T08:00:00.000Z -Invalid Date -Invalid Date -Invalid Date diff --git a/test/Date/DateCtr.js b/test/Date/DateCtr.js index 3927855d50f..d73318afc91 100644 --- a/test/Date/DateCtr.js +++ b/test/Date/DateCtr.js @@ -3,22 +3,64 @@ // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- -function write(v) { WScript.Echo(v + ""); } - -var d; - -d = new Date("Thu Jan 10 05:30:01 UTC+0530 1970"); write(d.toISOString()); -d = new Date("1974"); write(d.toISOString()); -d = new Date(1974); write(d.toISOString()); -d = new Date(1974, 9); write(d.toISOString()); -d = new Date(1974, 9, 24); write(d.toISOString()); -d = new Date(1974, 9, 24, 0); write(d.toISOString()); -d = new Date(1974, 9, 24, 0, 20); write(d.toISOString()); -d = new Date(1974, 9, 24, 0, 20, 30); write(d.toISOString()); -d = new Date(1974, 9, 24, 0, 20, 30, 40); write(d.toISOString()); -d = new Date(1974, 9, 24, 0, 20, 30, 40, 50); write(d.toISOString()); -d = new Date(2000, -1200001); write(d.toISOString()); // Make sure there is no AV for negative month (WOOB 1140748). -d = new Date(2000, -1); write(d.toISOString()); // Check correctness when month is negative. -d = new Date("", 1e81); write(d); // WOOB 1139099 -d = new Date(); d.setSeconds(Number.MAX_VALUE); write(d); // WOOB 1142298 -d = new Date(); d.setSeconds(-Number.MAX_VALUE); write(d); // WOOB 1142298 +var RESULTS = [ + "1970-01-10T00:00:01.000Z", + "1974-01-01T00:00:00.000Z", + "1970-01-01T00:00:01.974Z", + "1974-10-01T07:00:00.000Z", + "1974-10-24T07:00:00.000Z", + "1974-10-24T07:00:00.000Z", + "1974-10-24T07:20:00.000Z", + "1974-10-24T07:20:30.000Z", + "1974-10-24T07:20:30.040Z", + "1974-10-24T07:20:30.040Z", + "-098001-12-01T08:00:00.000Z", + "1999-12-01T08:00:00.000Z", + "Invalid Date", + "Invalid Date", + "Invalid Date" ]; + + +var index = 0; +function CHECK(str) +{ + if (str != RESULTS[index]) + { + console.log(str + " != " + RESULTS[index]); + console.log("FAIL"); + return false; + } + index++; + return true; +} + +function Test() +{ + var d; + + d = new Date("Thu Jan 10 05:30:01 UTC+0530 1970"); if (!CHECK(d.toISOString())) return; + d = new Date("1974"); if (!CHECK(d.toISOString())) return; + d = new Date(1974); if (!CHECK(d.toISOString())) return; + d = new Date(1974, 9); if (!CHECK(d.toISOString())) return; + d = new Date(1974, 9, 24); if (!CHECK(d.toISOString())) return; + d = new Date(1974, 9, 24, 0); if (!CHECK(d.toISOString())) return; + d = new Date(1974, 9, 24, 0, 20); if (!CHECK(d.toISOString())) return; + d = new Date(1974, 9, 24, 0, 20, 30); if (!CHECK(d.toISOString())) return; + d = new Date(1974, 9, 24, 0, 20, 30, 40); if (!CHECK(d.toISOString())) return; + d = new Date(1974, 9, 24, 0, 20, 30, 40, 50); if (!CHECK(d.toISOString())) return; + + if (!WScript.Platform || WScript.Platform.OS == "win32") + { + d = new Date(2000, -1200001); if (!CHECK(d.toISOString())) return; // Make sure there is no AV for negative month (WOOB 1140748). + } else { + index++; // xplat DST info for BC doesn't match to Windows. + } + + d = new Date(2000, -1); if (!CHECK(d.toISOString())) return; // Check correctness when month is negative. + d = new Date("", 1e81); if (!CHECK(d + "")) return; // WOOB 1139099 + d = new Date(); d.setSeconds(Number.MAX_VALUE); if (!CHECK(d + "")) return; // WOOB 1142298 + d = new Date(); d.setSeconds(-Number.MAX_VALUE); if (!CHECK(d + "")) return; // WOOB 1142298 + console.log("PASS"); +} + +Test(); diff --git a/test/Date/rlexe.xml b/test/Date/rlexe.xml index 578224f0024..2d9f37f5512 100644 --- a/test/Date/rlexe.xml +++ b/test/Date/rlexe.xml @@ -3,7 +3,6 @@ DateCtr.js - DateCtr.baseline exclude_jenkins @@ -13,8 +12,8 @@ DateGetSet.js DateGetSet.baseline - - exclude_jenkins,exclude_mac + + exclude_jenkins,exclude_xplat @@ -30,7 +29,8 @@ DateParse2.js DateParse2.v5.baseline - exclude_jenkins + + exclude_jenkins,exclude_xplat @@ -45,7 +45,7 @@ parseISO.baseline - exclude_jenkins,exclude_mac,Slow + exclude_jenkins,exclude_xplat,Slow @@ -120,8 +120,7 @@ xplatInterval.js - - exclude_jenkins,Slow + Slow