blob: 6d18f54541673932d2b81141b125c1041603e381 [file] [log] [blame]
/*
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
* Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
* Copyright (C) 2009 Google Inc. All rights reserved.
* Copyright (C) 2007-2009 Torch Mobile, Inc.
* Copyright (C) 2010 &yet, LLC. (nate@andyet.net)
*
* 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.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Alternatively, the contents of this file may be used under the terms
* of either the Mozilla Public License Version 1.1, found at
* http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
* License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
* (the "GPL"), in which case the provisions of the MPL or the GPL are
* applicable instead of those above. If you wish to allow use of your
* version of this file only under the terms of one of those two
* licenses (the MPL or the GPL) and not to allow others to use your
* version of this file under the LGPL, indicate your decision by
* deletingthe provisions above and replace them with the notice and
* other provisions required by the MPL or the GPL, as the case may be.
* If you do not delete the provisions above, a recipient may use your
* version of this file under any of the LGPL, the MPL or the GPL.
* Copyright 2006-2008 the V8 project authors. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/platform/wtf/date_math.h"
#include <limits.h>
#include <math.h>
#include <stdlib.h>
#include <time.h>
#include <unicode/basictz.h>
#include <unicode/timezone.h>
#include <algorithm>
#include <array>
#include <limits>
#include <memory>
#include "third_party/blink/renderer/platform/wtf/math_extras.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
/* Constants */
static const double kMinimumECMADateInMs = -8640000000000000.0;
static const double kMaximumECMADateInMs = 8640000000000000.0;
// Day of year for the first day of each month, where index 0 is January, and
// day 0 is January 1. First for non-leap years, then for leap years.
static const std::array<std::array<int, 12>, 2> kFirstDayOfMonth = {
{{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}}};
bool IsLeapYear(int year) {
if (year % 4 != 0)
return false;
if (year % 400 == 0)
return true;
if (year % 100 == 0)
return false;
return true;
}
static inline int DaysInYear(int year) {
return 365 + IsLeapYear(year);
}
static 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 const int kLeapDaysBefore1971By4Rule = 1970 / 4;
static const int kExcludedLeapDaysBefore1971By100Rule = 1970 / 100;
static const int kLeapDaysBefore1971By400Rule = 1970 / 400;
const double year_minus_one = year - 1;
const double years_to_add_by4_rule =
floor(year_minus_one / 4.0) - kLeapDaysBefore1971By4Rule;
const double years_to_exclude_by100_rule =
floor(year_minus_one / 100.0) - kExcludedLeapDaysBefore1971By100Rule;
const double years_to_add_by400_rule =
floor(year_minus_one / 400.0) - kLeapDaysBefore1971By400Rule;
return 365.0 * (year - 1970) + years_to_add_by4_rule -
years_to_exclude_by100_rule + years_to_add_by400_rule;
}
static double MsToDays(double ms) {
return base::Milliseconds(ms).InDaysFloored();
}
int MsToYear(double ms) {
DCHECK(std::isfinite(ms));
DCHECK_GE(ms, kMinimumECMADateInMs);
DCHECK_LE(ms, kMaximumECMADateInMs);
static constexpr double kMsPerDay = base::Time::kMillisecondsPerDay;
int approx_year = static_cast<int>(floor(ms / (kMsPerDay * 365.2425)) + 1970);
double ms_from_approx_year_to1970 =
kMsPerDay * DaysFrom1970ToYear(approx_year);
if (ms_from_approx_year_to1970 > ms)
return approx_year - 1;
if (ms_from_approx_year_to1970 + kMsPerDay * DaysInYear(approx_year) <= ms)
return approx_year + 1;
return approx_year;
}
int DayInYear(double ms, int year) {
return static_cast<int>(MsToDays(ms) - DaysFrom1970ToYear(year));
}
int MonthFromDayInYear(int day_in_year, bool leap_year) {
const int d = day_in_year;
int step;
if (d < (step = 31))
return 0;
step += (leap_year ? 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;
}
static inline bool CheckMonth(int day_in_year,
int& start_day_of_this_month,
int& start_day_of_next_month,
int days_in_this_month) {
start_day_of_this_month = start_day_of_next_month;
start_day_of_next_month += days_in_this_month;
return (day_in_year <= start_day_of_next_month);
}
int DayInMonthFromDayInYear(int day_in_year, bool leap_year) {
const int d = day_in_year;
int step;
int next = 30;
if (d <= next)
return d + 1;
const int days_in_feb = (leap_year ? 29 : 28);
if (CheckMonth(d, step, next, days_in_feb))
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;
}
int DayInYear(int year, int month, int day) {
return kFirstDayOfMonth[IsLeapYear(year)][month] + day - 1;
}
double DateToDaysFrom1970(int year, int month, int day) {
year += month / 12;
month %= 12;
if (month < 0) {
month += 12;
--year;
}
double yearday = floor(DaysFrom1970ToYear(year));
DCHECK((year >= 1970 && yearday >= 0) || (year < 1970 && yearday < 0));
return yearday + DayInYear(year, month, day);
}
base::TimeDelta ConvertToLocalTime(base::Time time) {
double ms = time.InMillisecondsFSinceUnixEpoch();
std::unique_ptr<icu::TimeZone> timezone(icu::TimeZone::createDefault());
int32_t raw_offset, dst_offset;
UErrorCode status = U_ZERO_ERROR;
timezone->getOffset(ms, false, raw_offset, dst_offset, status);
DCHECK(U_SUCCESS(status));
return base::Milliseconds(ms + static_cast<double>(raw_offset + dst_offset));
}
} // namespace blink