/* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) * Copyright (C) 2006-2020 Apple Inc. All rights reserved. * Copyright (C) 2009 Google Inc. All rights reserved. * Copyright (C) 2010 Research In Motion Limited. All rights reserved. * * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * */ #pragma once #include #include #include #include #include #include namespace WTF { enum TimeType { UTCTime = 0, LocalTime }; struct LocalTimeOffset { WTF_MAKE_STRUCT_FAST_ALLOCATED; LocalTimeOffset() = default; constexpr LocalTimeOffset(bool isDST, int offset) : isDST(isDST) , offset(offset) { } bool operator==(const LocalTimeOffset& other) { return isDST == other.isDST && offset == other.offset; } bool operator!=(const LocalTimeOffset& other) { return isDST != other.isDST || offset != other.offset; } bool isDST { false }; int offset { 0 }; }; void initializeDates(); int equivalentYearForDST(int year); // Not really math related, but this is currently the only shared place to put these. WTF_EXPORT_PRIVATE double parseES5DateFromNullTerminatedCharacters(const char* dateString, bool& isLocalTime); WTF_EXPORT_PRIVATE double parseDateFromNullTerminatedCharacters(const char* dateString); WTF_EXPORT_PRIVATE double parseDateFromNullTerminatedCharacters(const char* dateString, bool& isLocalTime); // dayOfWeek: [0, 6] 0 being Monday, day: [1, 31], month: [0, 11], year: ex: 2011, hours: [0, 23], minutes: [0, 59], seconds: [0, 59], utcOffset: [-720,720]. WTF_EXPORT_PRIVATE String makeRFC2822DateString(unsigned dayOfWeek, unsigned day, unsigned month, unsigned year, unsigned hours, unsigned minutes, unsigned seconds, int utcOffset); inline double jsCurrentTime() { // JavaScript doesn't recognize fractions of a millisecond. return floor(WallTime::now().secondsSinceEpoch().milliseconds()); } extern WTF_EXPORT_PRIVATE const char* const weekdayName[7]; extern WTF_EXPORT_PRIVATE const char* const monthName[12]; extern WTF_EXPORT_PRIVATE const char* const monthFullName[12]; extern WTF_EXPORT_PRIVATE const int firstDayOfMonth[2][12]; static constexpr double hoursPerDay = 24.0; static constexpr double minutesPerHour = 60.0; static constexpr double secondsPerMinute = 60.0; static constexpr double msPerSecond = 1000.0; static constexpr double msPerMonth = 2592000000.0; static constexpr double secondsPerHour = secondsPerMinute * minutesPerHour; static constexpr double secondsPerDay = secondsPerHour * hoursPerDay; static constexpr double msPerMinute = msPerSecond * secondsPerMinute; static constexpr double msPerHour = msPerSecond * secondsPerHour; static constexpr double msPerDay = msPerSecond * secondsPerDay; static constexpr double maxUnixTime = 2145859200.0; // 12/31/2037 // ECMAScript asks not to support for a date of which total // millisecond value is larger than the following value. // See 15.9.1.14 of ECMA-262 5th edition. static constexpr double maxECMAScriptTime = 8.64E15; class TimeClippedPositiveMilliseconds { public: static constexpr int64_t hoursPerDay = 24; static constexpr int64_t minutesPerHour = 60; static constexpr int64_t secondsPerMinute = 60; static constexpr int64_t msPerSecond = 1000; static constexpr int64_t msPerMonth = 2592000000; static constexpr int64_t secondsPerHour = secondsPerMinute * minutesPerHour; static constexpr int64_t secondsPerDay = secondsPerHour * hoursPerDay; static constexpr int64_t msPerMinute = msPerSecond * secondsPerMinute; static constexpr int64_t msPerHour = msPerSecond * secondsPerHour; static constexpr int64_t msPerDay = msPerSecond * secondsPerDay; static constexpr int64_t maxECMAScriptTime = 8.64E15; explicit TimeClippedPositiveMilliseconds(int64_t value) : m_value(value) { ASSERT(value >= 0); } int64_t value() const { return m_value; } double asDouble() const { return static_cast(m_value); } private: int64_t m_value; }; inline double timeClip(double t) { if (std::abs(t) > maxECMAScriptTime) return std::numeric_limits::quiet_NaN(); return std::trunc(t) + 0.0; } inline double daysFrom1970ToYear(int year) { // The Gregorian Calendar rules for leap years: // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years. // However, every hundredth year is not a leap year. 1900 and 2100 are not leap years. // Every four hundred years, there's a leap year after all. 2000 and 2400 are leap years. static constexpr int leapDaysBefore1971By4Rule = 1970 / 4; static constexpr int excludedLeapDaysBefore1971By100Rule = 1970 / 100; static constexpr int leapDaysBefore1971By400Rule = 1970 / 400; const double yearMinusOne = static_cast(year) - 1; const double yearsToAddBy4Rule = floor(yearMinusOne / 4.0) - leapDaysBefore1971By4Rule; const double yearsToExcludeBy100Rule = floor(yearMinusOne / 100.0) - excludedLeapDaysBefore1971By100Rule; const double yearsToAddBy400Rule = floor(yearMinusOne / 400.0) - leapDaysBefore1971By400Rule; return 365.0 * (year - 1970.0) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule; } inline int64_t daysFrom1970ToYearTimeClippedPositive(int year) { static constexpr int leapDaysBefore1971By4Rule = 1970 / 4; static constexpr int excludedLeapDaysBefore1971By100Rule = 1970 / 100; static constexpr int leapDaysBefore1971By400Rule = 1970 / 400; ASSERT(year >= 1970); const int64_t yearMinusOne = year - 1; const int64_t yearsToAddBy4Rule = yearMinusOne / 4.0 - leapDaysBefore1971By4Rule; const int64_t yearsToExcludeBy100Rule = yearMinusOne / 100.0 - excludedLeapDaysBefore1971By100Rule; const int64_t yearsToAddBy400Rule = yearMinusOne / 400.0 - leapDaysBefore1971By400Rule; return 365 * (year - 1970) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule; } inline bool isLeapYear(int year) { if (year % 4 != 0) return false; if (year % 400 == 0) return true; if (year % 100 == 0) return false; return true; } inline int daysInYear(int year) { return 365 + isLeapYear(year); } inline double msToDays(double ms) { return floor(ms / msPerDay); } inline int64_t msToDays(TimeClippedPositiveMilliseconds ms) { return ms.value() / TimeClippedPositiveMilliseconds::msPerDay; } inline int dayInYear(int year, int month, int day) { return firstDayOfMonth[isLeapYear(year)][month] + day - 1; } inline int dayInYear(double ms, int year) { double result = msToDays(ms) - daysFrom1970ToYear(year); return std::isnan(result) ? 0 : static_cast(result); } inline int dayInYear(TimeClippedPositiveMilliseconds ms, int year) { return static_cast(msToDays(ms) - daysFrom1970ToYearTimeClippedPositive(year)); } // Returns the number of days from 1970-01-01 to the specified date. inline double dateToDaysFrom1970(int year, int month, int day) { year += month / 12; month %= 12; if (month < 0) { month += 12; --year; } double yearday = floor(daysFrom1970ToYear(year)); ASSERT((year >= 1970 && yearday >= 0) || (year < 1970 && yearday < 0)); return yearday + dayInYear(year, month, day); } inline int msToYear(double ms) { double msAsYears = std::floor(ms / (msPerDay * 365.2425)); if (std::isnan(msAsYears)) msAsYears = 0; int approxYear = static_cast(msAsYears + 1970); double msFromApproxYearTo1970 = msPerDay * daysFrom1970ToYear(approxYear); if (msFromApproxYearTo1970 > ms) return approxYear - 1; if (msFromApproxYearTo1970 + msPerDay * daysInYear(approxYear) <= ms) return approxYear + 1; return approxYear; } inline int msToMinutes(double ms) { double result = fmod(floor(ms / msPerMinute), minutesPerHour); if (result < 0) result += minutesPerHour; return static_cast(result); } inline int msToMinutes(TimeClippedPositiveMilliseconds ms) { int64_t result = (ms.value() / TimeClippedPositiveMilliseconds::msPerMinute) % TimeClippedPositiveMilliseconds::minutesPerHour; ASSERT(result >= 0); return static_cast(result); } inline int msToHours(double ms) { double result = fmod(floor(ms / msPerHour), hoursPerDay); if (result < 0) result += hoursPerDay; return static_cast(result); } inline int msToHours(TimeClippedPositiveMilliseconds ms) { int64_t result = (ms.value() / TimeClippedPositiveMilliseconds::msPerHour) % TimeClippedPositiveMilliseconds::hoursPerDay; ASSERT(result >= 0); return static_cast(result); } inline int msToSeconds(double ms) { double result = fmod(floor(ms / msPerSecond), secondsPerMinute); if (result < 0) result += secondsPerMinute; return static_cast(result); } inline int msToSeconds(TimeClippedPositiveMilliseconds ms) { int64_t result = ms.value() / TimeClippedPositiveMilliseconds::msPerSecond % TimeClippedPositiveMilliseconds::secondsPerMinute; ASSERT(result >= 0); return static_cast(result); } // 0: Sunday, 1: Monday, etc. inline int msToWeekDay(double ms) { int wd = (static_cast(msToDays(ms)) + 4) % 7; if (wd < 0) wd += 7; return wd; } inline int msToWeekDay(TimeClippedPositiveMilliseconds ms) { int result = (static_cast(msToDays(ms)) + 4) % 7; ASSERT(result >= 0); return result; } inline int monthFromDayInYear(int dayInYear, bool leapYear) { const int d = dayInYear; int step; if (d < (step = 31)) return 0; step += (leapYear ? 29 : 28); if (d < step) return 1; if (d < (step += 31)) return 2; if (d < (step += 30)) return 3; if (d < (step += 31)) return 4; if (d < (step += 30)) return 5; if (d < (step += 31)) return 6; if (d < (step += 31)) return 7; if (d < (step += 30)) return 8; if (d < (step += 31)) return 9; if (d < step + 30) return 10; return 11; } inline int dayInMonthFromDayInYear(int dayInYear, bool leapYear) { auto checkMonth = [] (int dayInYear, int& startDayOfThisMonth, int& startDayOfNextMonth, int daysInThisMonth) -> bool { startDayOfThisMonth = startDayOfNextMonth; startDayOfNextMonth += daysInThisMonth; return (dayInYear <= startDayOfNextMonth); }; const int d = dayInYear; int step; int next = 30; if (d <= next) return d + 1; const int daysInFeb = (leapYear ? 29 : 28); if (checkMonth(d, step, next, daysInFeb)) return d - step; if (checkMonth(d, step, next, 31)) return d - step; if (checkMonth(d, step, next, 30)) return d - step; if (checkMonth(d, step, next, 31)) return d - step; if (checkMonth(d, step, next, 30)) return d - step; if (checkMonth(d, step, next, 31)) return d - step; if (checkMonth(d, step, next, 31)) return d - step; if (checkMonth(d, step, next, 30)) return d - step; if (checkMonth(d, step, next, 31)) return d - step; if (checkMonth(d, step, next, 30)) return d - step; step = next; return d - step; } // Returns combined offset in millisecond (UTC + DST). WTF_EXPORT_PRIVATE LocalTimeOffset calculateLocalTimeOffset(double utcInMilliseconds, TimeType = UTCTime); } // namespace WTF using WTF::isLeapYear; using WTF::dateToDaysFrom1970; using WTF::dayInMonthFromDayInYear; using WTF::dayInYear; using WTF::minutesPerHour; using WTF::monthFromDayInYear; using WTF::msPerDay; using WTF::msPerHour; using WTF::msPerMinute; using WTF::msPerSecond; using WTF::msToYear; using WTF::msToDays; using WTF::msToMinutes; using WTF::msToHours; using WTF::secondsPerDay; using WTF::secondsPerMinute; using WTF::parseDateFromNullTerminatedCharacters; using WTF::makeRFC2822DateString; using WTF::LocalTimeOffset; using WTF::calculateLocalTimeOffset; using WTF::timeClip; using WTF::jsCurrentTime;