| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #pragma once
- struct _SYSTEMTIME;
- using namespace PlatformAgnostic;
- namespace Js {
- struct SZS;
- class DateImplementation:
- public DateUtilities
- {
- friend class JavascriptDate;
- friend class JavascriptVariantDate;
- typedef struct tm TM;
- static const short kpstDstRuleChangeYear = 2007;
- static const double ktvMax;
- static const double ktvMin;
- static const int g_mpytyear[14];
- static const int g_mpytyearpost2006[14] ;
- double GetMilliSeconds();
- static double NowInMilliSeconds(ScriptContext* scriptContext);
- static double NowFromHiResTimer(ScriptContext * scriptContext);
- static double UtcTimeFromStr(ScriptContext *scriptContext, JavascriptString *pParseString);
- static double DoubleToTvUtc(double tv);
- private:
- DateImplementation(VirtualTableInfoCtorEnum) { m_modified = false; }
- DateImplementation(double value);
- BEGIN_ENUM_BYTE(DateStringFormat)
- Default,
- Locale,
- GMT,
- Lim,
- END_ENUM_BYTE()
- BEGIN_ENUM_BYTE(DateValueType)
- Local = 1, // Whether tvLcl and tzd are valid.
- YearMonthDayLocal = 2,
- YearMonthDayUTC = 4,
- NotNaN = 8,
- END_ENUM_BYTE()
- // date data
- BEGIN_ENUM_BYTE(DateData)
- // WARNING: There are tables that depend on this order.
- Year,
- FullYear,
- Month,
- Date,
- Hours,
- Minutes,
- Seconds,
- Milliseconds,
- Day,
- TimezoneOffset,
- Lim,
- END_ENUM_BYTE()
- public:
- // Flags which control if date or time or both need to be included.
- BEGIN_ENUM_BYTE(DateTimeFlag)
- None = 0x00,
- NoTime = 0x01,
- NoDate = 0x02,
- END_ENUM_BYTE()
- // Time zone descriptor.
- struct TZD
- {
- Field(int) minutes;
- Field(int) offset;
- // Indicates whether Daylight savings
- Field(bool) fDst;
- };
- template <class ScriptContext>
- static double GetTvLcl(double tv, ScriptContext * scriptContext, TZD *ptzd = nullptr);
- template <class ScriptContext>
- static double GetTvUtc(double tv, ScriptContext * scriptContext);
- static bool UtcTimeFromStrCore(
- __in_ecount_z(ulength) const char16 *psz,
- unsigned int ulength,
- double &retVal,
- ScriptContext * const scriptContext);
- // Used for VT_DATE conversions
- //------------------------------------
- //Convert a utc time to a variant date.
- //------------------------------------
- static double VarDateFromJsUtcTime(double dbl, ScriptContext * scriptContext);
- static double JsUtcTimeFromVarDate(double dbl, ScriptContext *scriptContext);
- void SetTvUtc(double tv);
- bool IsModified() { return m_modified; }
- void ClearModified() { m_modified = false; }
- private:
- JavascriptString* GetString(DateStringFormat dsf, ScriptContext* requestContext,
- DateTimeFlag noDateTime = DateTimeFlag::None);
- JavascriptString* GetISOString(ScriptContext* requestContext);
- void GetDateComponent(CompoundString *bs, DateData componentType, int adjust,
- ScriptContext* requestContext);
- void SetTvLcl(double tv, ScriptContext* requestContext);
- double GetDateData(DateData dd, bool fUtc, ScriptContext* scriptContext);
- double SetDateData(Arguments args, DateData dd, bool fUtc, ScriptContext* scriptContext);
- static bool TryParseDecimalDigits(
- const char16 *const str,
- const size_t length,
- const size_t startIndex,
- size_t numDigits,
- int &value);
- static bool TryParseMilliseconds(
- const char16 *const str,
- const size_t length,
- const size_t startIndex,
- int &value,
- size_t &foundDigits);
- static bool TryParseTwoDecimalDigits(
- const char16 *const str,
- const size_t length,
- const size_t startIndex,
- int &value,
- bool canHaveTrailingDigit = false);
- // Tries to parse the string as an ISO-format date/time as defined in the spec. Returns false if the string is not in
- // ISO format.
- static bool TryParseIsoString(const char16 *const str, const size_t length, double &timeValue, ScriptContext *scriptContext);
- static JavascriptString* ConvertVariantDateToString(double variantDateDouble, ScriptContext* scriptContext);
- static JavascriptString* GetDateDefaultString(DateTime::YMD *pymd, TZD *ptzd,DateTimeFlag noDateTime,ScriptContext* scriptContext);
- static JavascriptString* GetDateGmtString(DateTime::YMD *pymd,ScriptContext* scriptContext);
- #ifdef ENABLE_GLOBALIZATION // todo-xplat: Implement this ICU?
- static JavascriptString* GetDateLocaleString(DateTime::YMD *pymd, TZD *ptzd, DateTimeFlag noDateTime,ScriptContext* scriptContext);
- #endif
- static double DateFncUTC(ScriptContext* scriptContext, Arguments args);
- static bool FBig(char16 ch);
- static bool FDateDelimiter(char16 ch);
- bool IsNaN()
- {
- if (m_grfval & DateValueType::NotNaN)
- {
- return false;
- }
- if (JavascriptNumber::IsNan(m_tvUtc))
- {
- return true;
- }
- m_grfval |= DateValueType::NotNaN;
- return false;
- }
- ///------------------------------------------------------------------------------
- /// Make sure m_tvLcl is valid. (Shared with hybrid debugging, which may use a fake scriptContext.)
- ///------------------------------------------------------------------------------
- template <class ScriptContext>
- inline void EnsureTvLcl(ScriptContext* scriptContext)
- {
- if (!(m_grfval & DateValueType::Local))
- {
- m_tvLcl = GetTvLcl(m_tvUtc, scriptContext, &m_tzd);
- m_grfval |= DateValueType::Local;
- }
- }
- ///------------------------------------------------------------------------------
- /// Make sure m_ymdLcl is valid. (Shared with hybrid debugging, which may use a fake scriptContext.)
- ///------------------------------------------------------------------------------
- template <class ScriptContext>
- inline void EnsureYmdLcl(ScriptContext* scriptContext)
- {
- if (m_grfval & DateValueType::YearMonthDayLocal)
- {
- return;
- }
- EnsureTvLcl(scriptContext);
- GetYmdFromTv(m_tvLcl, &m_ymdLcl);
- m_grfval |= DateValueType::YearMonthDayLocal;
- }
- ///------------------------------------------------------------------------------
- /// Make sure m_ymdUtc is valid.
- ///------------------------------------------------------------------------------
- inline void EnsureYmdUtc(void)
- {
- if (m_grfval & DateValueType::YearMonthDayUTC)
- {
- return;
- }
- GetYmdFromTv(m_tvUtc, &m_ymdUtc);
- m_grfval |= DateValueType::YearMonthDayUTC;
- }
- inline Var GetFullYear(ScriptContext* requestContext)
- {
- EnsureYmdLcl(requestContext);
- return JavascriptNumber::ToVar(m_ymdLcl.year, requestContext);
- }
- inline Var GetYear(ScriptContext* requestContext)
- {
- EnsureYmdLcl(requestContext);
- // WOOB bug 1099381: ES5 spec B.2.4: getYear() must return YearFromTime() - 1900.
- // Note that negative value is OK for the spec.
- int value = m_ymdLcl.year - 1900;
- return JavascriptNumber::ToVar(value, requestContext);
- }
- inline Var GetMonth(ScriptContext* requestContext)
- {
- EnsureYmdLcl(requestContext);
- return JavascriptNumber::ToVar(m_ymdLcl.mon, requestContext);
- }
- inline Var GetDate(ScriptContext* requestContext)
- {
- EnsureYmdLcl(requestContext);
- return JavascriptNumber::ToVar(m_ymdLcl.mday + 1, requestContext);
- }
- inline Var GetDay(ScriptContext* requestContext)
- {
- EnsureYmdLcl(requestContext);
- return JavascriptNumber::ToVar(m_ymdLcl.wday, requestContext);
- }
- inline Var GetHours(ScriptContext* requestContext)
- {
- EnsureYmdLcl(requestContext);
- return JavascriptNumber::ToVar((m_ymdLcl.time / 3600000)%24, requestContext);
- }
- inline Var GetMinutes(ScriptContext* requestContext)
- {
- EnsureYmdLcl(requestContext);
- return JavascriptNumber::ToVar((m_ymdLcl.time / 60000) % 60, requestContext);
- }
- inline Var GetSeconds(ScriptContext* requestContext)
- {
- EnsureYmdLcl(requestContext);
- return JavascriptNumber::ToVar((m_ymdLcl.time / 1000) % 60, requestContext);
- }
- inline Var GetDateMilliSeconds(ScriptContext* requestContext)
- {
- EnsureYmdLcl(requestContext);
- return JavascriptNumber::ToVar(m_ymdLcl.time % 1000, requestContext);
- }
- template <class StringBuilder, class ScriptContext, class NewStringBuilderFunc>
- StringBuilder* GetDiagValueString(ScriptContext* scriptContext, NewStringBuilderFunc newStringBuilder);
- template <class StringBuilder, class ScriptContext, class NewStringBuilderFunc>
- static StringBuilder* ConvertVariantDateToString(double dbl, ScriptContext* scriptContext, NewStringBuilderFunc newStringBuilder);
- template <class StringBuilder, class ScriptContext, class NewStringBuilderFunc>
- static StringBuilder* GetDateDefaultString(DateTime::YMD *pymd, TZD *ptzd, DateTimeFlag noDateTime, ScriptContext* scriptContext, NewStringBuilderFunc newStringBuilder);
- private:
- Field(double) m_tvUtc;
- Field(double) m_tvLcl;
- FieldNoBarrier(DateTime::YMD) m_ymdUtc;
- FieldNoBarrier(DateTime::YMD) m_ymdLcl;
- Field(TZD) m_tzd;
- Field(uint32) m_grfval; // Which fields are valid. m_tvUtc is always valid.
- Field(bool) m_modified : 1; // Whether SetDateData was called on this class
- friend JavascriptDate;
- };
- ///
- /// Use tv as the UTC time and return the corresponding local time. (Shared with hybrid debugging, which may use a fake scriptContext.)
- ///
- template <class ScriptContext>
- double DateImplementation::GetTvLcl(double tv, ScriptContext *scriptContext, TZD *ptzd)
- {
- Assert(scriptContext);
- double tvLcl;
- if (nullptr != ptzd)
- {
- ptzd->minutes = 0;
- ptzd->fDst = FALSE;
- }
- // See if we're out of range before conversion (UTC time value must be within this range)
- if (JavascriptNumber::IsNan(tv) || tv < ktvMin || tv > ktvMax)
- {
- return JavascriptNumber::NaN;
- }
- int bias;
- int offset;
- bool isDaylightSavings;
- tvLcl = scriptContext->GetDaylightTimeHelper()->UtcToLocal(tv, bias, offset, isDaylightSavings);
- if (nullptr != ptzd)
- {
- ptzd->minutes = -bias;
- ptzd->offset = offset;
- ptzd->fDst = isDaylightSavings;
- }
- return tvLcl;
- }
- ///
- /// Use tv as the local time and return the corresponding UTC time. (Shared with hybrid debugging, which may use a fake scriptContext.)
- ///
- template <class ScriptContext>
- double DateImplementation::GetTvUtc(double tv, ScriptContext *scriptContext)
- {
- Assert(scriptContext);
- double tvUtc;
- if (JavascriptNumber::IsNan(tv) || !NumberUtilities::IsFinite(tv))
- {
- return JavascriptNumber::NaN;
- }
- tvUtc = scriptContext->GetDaylightTimeHelper()->LocalToUtc(tv);
- // See if we're out of range after conversion (UTC time value must be within this range)
- if (JavascriptNumber::IsNan(tvUtc) || !NumberUtilities::IsFinite(tv) || tvUtc < ktvMin || tvUtc > ktvMax)
- {
- return JavascriptNumber::NaN;
- }
- return tvUtc;
- }
- //
- // Get diag value display for hybrid debugging.
- // StringBuilder: A Js::StringBuilder/CompoundString like class, used to build strings.
- // ScriptContext: A Js::ScriptContext like class, which provides fields needed by Date stringify.
- // NewStringBuilderFunc: A function that returns a StringBuilder*, used to create a StringBuilder.
- //
- template <class StringBuilder, class ScriptContext, class NewStringBuilderFunc>
- StringBuilder* DateImplementation::GetDiagValueString(ScriptContext* scriptContext, NewStringBuilderFunc newStringBuilder)
- {
- if (JavascriptNumber::IsNan(m_tvUtc))
- {
- StringBuilder* bs = newStringBuilder(0);
- bs->Append(JS_DISPLAY_STRING_INVALID_DATE);
- return bs;
- }
- EnsureYmdLcl(scriptContext);
- return GetDateDefaultString<StringBuilder>(&m_ymdLcl, &m_tzd, DateTimeFlag::None, scriptContext, newStringBuilder);
- }
- template <class StringBuilder, class ScriptContext, class NewStringBuilderFunc>
- StringBuilder* DateImplementation::ConvertVariantDateToString(double dbl, ScriptContext* scriptContext, NewStringBuilderFunc newStringBuilder)
- {
- TZD tzd;
- DateTime::YMD ymd;
- double tv = GetTvUtc(JsLocalTimeFromVarDate(dbl), scriptContext);
- tv = GetTvLcl(tv, scriptContext, &tzd);
- if (JavascriptNumber::IsNan(tv))
- {
- StringBuilder* bs = newStringBuilder(0);
- bs->Append(JS_DISPLAY_STRING_NAN);
- return bs;
- }
- GetYmdFromTv(tv, &ymd);
- return GetDateDefaultString<StringBuilder>(&ymd, &tzd, DateTimeFlag::None, scriptContext, newStringBuilder);
- }
- const auto ConvertUInt32ToString_ZeroPad_4 = [](const uint32 value, char16 *const buffer, const CharCount charCapacity)
- {
- Assert(charCapacity >= 4);
- if (value < 10)
- {
- buffer[0] = _u('0');
- buffer[1] = _u('0');
- buffer[2] = _u('0');
- buffer[3] = static_cast<char16>(value + _u('0'));
- buffer[4] = 0;
- }
- else if (value < 100)
- {
- buffer[0] = _u('0');
- buffer[1] = _u('0');
- buffer[2] = static_cast<char16>((value / 10) + _u('0'));
- buffer[3] = static_cast<char16>((value % 10) + _u('0'));
- buffer[4] = 0;
- }
- else if (value < 1000)
- {
- buffer[0] = _u('0');
- buffer[1] = static_cast<char16>((value / 100) + _u('0'));
- buffer[2] = static_cast<char16>(((value / 10) % 10) + _u('0'));
- buffer[3] = static_cast<char16>((value % 10) + _u('0'));
- buffer[4] = 0;
- }
- else
- {
- const errno_t err = _ultow_s(value, buffer, charCapacity, 10);
- Assert(err == 0);
- }
- };
- //
- // Get default date string, shared with hybrid debugging.
- // StringBuilder: A Js::StringBuilder/CompoundString like class, used to build strings.
- // ScriptContext: A Js::ScriptContext like class, which provides fields needed by Date stringify.
- // NewStringBuilderFunc: A function that returns a StringBuilder*, used to create a StringBuilder.
- //
- template <class StringBuilder, class ScriptContext, class NewStringBuilderFunc>
- StringBuilder* DateImplementation::GetDateDefaultString(DateTime::YMD *pymd, TZD *ptzd, DateTimeFlag noDateTime, ScriptContext* scriptContext, NewStringBuilderFunc newStringBuilder)
- {
- int hour, min;
- StringBuilder* const bs = newStringBuilder(72);
- const auto ConvertUInt16ToString_ZeroPad_2 = [](const uint16 value, char16 *const buffer, const CharCount charCapacity)
- {
- const charcount_t cchWritten = NumberUtilities::UInt16ToString(value, buffer, charCapacity, 2);
- Assert(cchWritten != 0);
- };
- const auto ConvertLongToString = [](const int32 value, char16 *const buffer, const CharCount charCapacity)
- {
- const errno_t err = _ltow_s(value, buffer, charCapacity, 10);
- Assert(err == 0);
- };
- // PART 1 - DATE part
- if( !(noDateTime & DateTimeFlag::NoDate))
- {
- bs->AppendChars(g_rgpszDay[pymd->wday]);
- bs->AppendChars(_u(' '));
- bs->AppendChars(g_rgpszMonth[pymd->mon]);
- bs->AppendChars(_u(' '));
- // sz - as %02d - output is "01" to "31"
- bs->AppendChars(static_cast<WORD>(pymd->mday + 1), 2, ConvertUInt16ToString_ZeroPad_2);
- bs->AppendChars(_u(' '));
- // year is directly after day, month, daydigit for IE11+
- if ((pymd->year) > 0)
- {
- bs->AppendChars(pymd->year, 10, ConvertUInt32ToString_ZeroPad_4);
- }
- else
- {
- int positiveYear = -(pymd->year); // pymd->year is negative
- bs->AppendChars(_u('-'));
- bs->AppendChars(positiveYear, 10, ConvertUInt32ToString_ZeroPad_4);
- }
- if(!(noDateTime & DateTimeFlag::NoTime))
- {
- // append a space to delimit PART 2 - if to be outputted
- bs->AppendChars(_u(' '));
- }
- }
- // PART 2 - TIME part
- if(!(noDateTime & DateTimeFlag::NoTime))
- {
- // sz - as %02d - HOUR
- bs->AppendChars(static_cast<WORD>(pymd->time / 3600000), 2, ConvertUInt16ToString_ZeroPad_2);
- bs->AppendChars(_u(':'));
- // sz - as %02d - MINUTE
- bs->AppendChars(static_cast<WORD>((pymd->time / 60000) % 60), 2, ConvertUInt16ToString_ZeroPad_2);
- bs->AppendChars(_u(':'));
- // sz - as %02d - SECOND
- bs->AppendChars(static_cast<WORD>((pymd->time / 1000) % 60), 2, ConvertUInt16ToString_ZeroPad_2);
- bs->AppendChars(_u(" GMT"));
- // IE11+
- min = ptzd->offset;
- if (min < 0)
- {
- bs->AppendChars(_u('-'));
- min = -min;
- }
- else
- {
- bs->AppendChars(_u('+'));
- }
- hour = min / 60;
- min %= 60;
- // sz - as %02d - HOUR
- bs->AppendChars(static_cast<WORD>(hour), 2, ConvertUInt16ToString_ZeroPad_2);
- // sz - as %02d - MIN
- bs->AppendChars(static_cast<WORD>(min), 2, ConvertUInt16ToString_ZeroPad_2);
- bs->AppendChars(_u(" ("));
- // check the IsDaylightSavings?
- if (ptzd->fDst == false)
- {
- size_t nameLength;
- const WCHAR *const standardName = scriptContext->GetStandardName(&nameLength, pymd);
- bs->AppendChars(standardName, static_cast<CharCount>(nameLength));
- }
- else
- {
- size_t nameLength;
- const WCHAR *const daylightName = scriptContext->GetDaylightName(&nameLength, pymd);
- bs->AppendChars(daylightName, static_cast<CharCount>(nameLength));
- }
- bs->AppendChars(_u(')'));
- }
- return bs;
- }
- } // namespace Js
|