mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
715 lines
21 KiB
C++
715 lines
21 KiB
C++
/*
|
|
* Copyright (C) 2009 Google Inc. 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 "sky/engine/platform/DateComponents.h"
|
|
|
|
#include <limits.h>
|
|
#include "sky/engine/wtf/ASCIICType.h"
|
|
#include "sky/engine/wtf/DateMath.h"
|
|
#include "sky/engine/wtf/MathExtras.h"
|
|
#include "sky/engine/wtf/text/WTFString.h"
|
|
|
|
namespace blink {
|
|
|
|
// HTML5 specification defines minimum week of year is one.
|
|
const int DateComponents::minimumWeekNumber = 1;
|
|
|
|
// HTML5 specification defines maximum week of year is 53.
|
|
const int DateComponents::maximumWeekNumber = 53;
|
|
|
|
static const int maximumMonthInMaximumYear = 8; // This is September, since months are 0 based.
|
|
static const int maximumDayInMaximumMonth = 13;
|
|
static const int maximumWeekInMaximumYear = 37; // The week of 275760-09-13
|
|
|
|
static const int daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
|
|
|
// 'month' is 0-based.
|
|
static int maxDayOfMonth(int year, int month)
|
|
{
|
|
if (month != 1) // February?
|
|
return daysInMonth[month];
|
|
return isLeapYear(year) ? 29 : 28;
|
|
}
|
|
|
|
// 'month' is 0-based.
|
|
static int dayOfWeek(int year, int month, int day)
|
|
{
|
|
int shiftedMonth = month + 2;
|
|
// 2:January, 3:Feburuary, 4:March, ...
|
|
|
|
// Zeller's congruence
|
|
if (shiftedMonth <= 3) {
|
|
shiftedMonth += 12;
|
|
year--;
|
|
}
|
|
// 4:March, ..., 14:January, 15:February
|
|
|
|
int highYear = year / 100;
|
|
int lowYear = year % 100;
|
|
// We add 6 to make the result Sunday-origin.
|
|
int result = (day + 13 * shiftedMonth / 5 + lowYear + lowYear / 4 + highYear / 4 + 5 * highYear + 6) % 7;
|
|
return result;
|
|
}
|
|
|
|
int DateComponents::weekDay() const
|
|
{
|
|
return dayOfWeek(m_year, m_month, m_monthDay);
|
|
}
|
|
|
|
int DateComponents::maxWeekNumberInYear() const
|
|
{
|
|
int day = dayOfWeek(m_year, 0, 1); // January 1.
|
|
return day == Thursday || (day == Wednesday && isLeapYear(m_year)) ? maximumWeekNumber : maximumWeekNumber - 1;
|
|
}
|
|
|
|
static unsigned countDigits(const String& src, unsigned start)
|
|
{
|
|
unsigned index = start;
|
|
for (; index < src.length(); ++index) {
|
|
if (!isASCIIDigit(src[index]))
|
|
break;
|
|
}
|
|
return index - start;
|
|
}
|
|
|
|
// Very strict integer parser. Do not allow leading or trailing whitespace unlike charactersToIntStrict().
|
|
static bool toInt(const String& src, unsigned parseStart, unsigned parseLength, int& out)
|
|
{
|
|
if (parseStart + parseLength > src.length() || !parseLength)
|
|
return false;
|
|
int value = 0;
|
|
unsigned current = parseStart;
|
|
unsigned end = current + parseLength;
|
|
|
|
// We don't need to handle negative numbers for ISO 8601.
|
|
for (; current < end; ++current) {
|
|
if (!isASCIIDigit(src[current]))
|
|
return false;
|
|
int digit = src[current] - '0';
|
|
if (value > (INT_MAX - digit) / 10) // Check for overflow.
|
|
return false;
|
|
value = value * 10 + digit;
|
|
}
|
|
out = value;
|
|
return true;
|
|
}
|
|
|
|
bool DateComponents::parseYear(const String& src, unsigned start, unsigned& end)
|
|
{
|
|
unsigned digitsLength = countDigits(src, start);
|
|
// Needs at least 4 digits according to the standard.
|
|
if (digitsLength < 4)
|
|
return false;
|
|
int year;
|
|
if (!toInt(src, start, digitsLength, year))
|
|
return false;
|
|
if (year < minimumYear() || year > maximumYear())
|
|
return false;
|
|
m_year = year;
|
|
end = start + digitsLength;
|
|
return true;
|
|
}
|
|
|
|
static bool withinHTMLDateLimits(int year, int month)
|
|
{
|
|
if (year < DateComponents::minimumYear())
|
|
return false;
|
|
if (year < DateComponents::maximumYear())
|
|
return true;
|
|
return month <= maximumMonthInMaximumYear;
|
|
}
|
|
|
|
static bool withinHTMLDateLimits(int year, int month, int monthDay)
|
|
{
|
|
if (year < DateComponents::minimumYear())
|
|
return false;
|
|
if (year < DateComponents::maximumYear())
|
|
return true;
|
|
if (month < maximumMonthInMaximumYear)
|
|
return true;
|
|
return monthDay <= maximumDayInMaximumMonth;
|
|
}
|
|
|
|
static bool withinHTMLDateLimits(int year, int month, int monthDay, int hour, int minute, int second, int millisecond)
|
|
{
|
|
if (year < DateComponents::minimumYear())
|
|
return false;
|
|
if (year < DateComponents::maximumYear())
|
|
return true;
|
|
if (month < maximumMonthInMaximumYear)
|
|
return true;
|
|
if (monthDay < maximumDayInMaximumMonth)
|
|
return true;
|
|
if (monthDay > maximumDayInMaximumMonth)
|
|
return false;
|
|
// (year, month, monthDay) = (maximumYear, maximumMonthInMaximumYear, maximumDayInMaximumMonth)
|
|
return !hour && !minute && !second && !millisecond;
|
|
}
|
|
|
|
bool DateComponents::addDay(int dayDiff)
|
|
{
|
|
ASSERT(m_monthDay);
|
|
|
|
int day = m_monthDay + dayDiff;
|
|
if (day > maxDayOfMonth(m_year, m_month)) {
|
|
day = m_monthDay;
|
|
int year = m_year;
|
|
int month = m_month;
|
|
int maxDay = maxDayOfMonth(year, month);
|
|
for (; dayDiff > 0; --dayDiff) {
|
|
++day;
|
|
if (day > maxDay) {
|
|
day = 1;
|
|
++month;
|
|
if (month >= 12) { // month is 0-origin.
|
|
month = 0;
|
|
++year;
|
|
}
|
|
maxDay = maxDayOfMonth(year, month);
|
|
}
|
|
}
|
|
if (!withinHTMLDateLimits(year, month, day))
|
|
return false;
|
|
m_year = year;
|
|
m_month = month;
|
|
} else if (day < 1) {
|
|
int month = m_month;
|
|
int year = m_year;
|
|
day = m_monthDay;
|
|
for (; dayDiff < 0; ++dayDiff) {
|
|
--day;
|
|
if (day < 1) {
|
|
--month;
|
|
if (month < 0) {
|
|
month = 11;
|
|
--year;
|
|
}
|
|
day = maxDayOfMonth(year, month);
|
|
}
|
|
}
|
|
if (!withinHTMLDateLimits(year, month, day))
|
|
return false;
|
|
m_year = year;
|
|
m_month = month;
|
|
} else {
|
|
if (!withinHTMLDateLimits(m_year, m_month, day))
|
|
return false;
|
|
}
|
|
m_monthDay = day;
|
|
return true;
|
|
}
|
|
|
|
bool DateComponents::addMinute(int minute)
|
|
{
|
|
// This function is used to adjust timezone offset. So m_year, m_month,
|
|
// m_monthDay have values between the lower and higher limits.
|
|
ASSERT(withinHTMLDateLimits(m_year, m_month, m_monthDay));
|
|
|
|
int carry;
|
|
// minute can be negative or greater than 59.
|
|
minute += m_minute;
|
|
if (minute > 59) {
|
|
carry = minute / 60;
|
|
minute = minute % 60;
|
|
} else if (minute < 0) {
|
|
carry = (59 - minute) / 60;
|
|
minute += carry * 60;
|
|
carry = -carry;
|
|
ASSERT(minute >= 0 && minute <= 59);
|
|
} else {
|
|
if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, minute, m_second, m_millisecond))
|
|
return false;
|
|
m_minute = minute;
|
|
return true;
|
|
}
|
|
|
|
int hour = m_hour + carry;
|
|
if (hour > 23) {
|
|
carry = hour / 24;
|
|
hour = hour % 24;
|
|
} else if (hour < 0) {
|
|
carry = (23 - hour) / 24;
|
|
hour += carry * 24;
|
|
carry = -carry;
|
|
ASSERT(hour >= 0 && hour <= 23);
|
|
} else {
|
|
if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, hour, minute, m_second, m_millisecond))
|
|
return false;
|
|
m_minute = minute;
|
|
m_hour = hour;
|
|
return true;
|
|
}
|
|
if (!addDay(carry))
|
|
return false;
|
|
if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, hour, minute, m_second, m_millisecond))
|
|
return false;
|
|
m_minute = minute;
|
|
m_hour = hour;
|
|
return true;
|
|
}
|
|
|
|
// Parses a timezone part, and adjust year, month, monthDay, hour, minute, second, millisecond.
|
|
bool DateComponents::parseTimeZone(const String& src, unsigned start, unsigned& end)
|
|
{
|
|
if (start >= src.length())
|
|
return false;
|
|
unsigned index = start;
|
|
if (src[index] == 'Z') {
|
|
end = index + 1;
|
|
return true;
|
|
}
|
|
|
|
bool minus;
|
|
if (src[index] == '+')
|
|
minus = false;
|
|
else if (src[index] == '-')
|
|
minus = true;
|
|
else
|
|
return false;
|
|
++index;
|
|
|
|
int hour;
|
|
int minute;
|
|
if (!toInt(src, index, 2, hour) || hour < 0 || hour > 23)
|
|
return false;
|
|
index += 2;
|
|
|
|
if (index >= src.length() || src[index] != ':')
|
|
return false;
|
|
++index;
|
|
|
|
if (!toInt(src, index, 2, minute) || minute < 0 || minute > 59)
|
|
return false;
|
|
index += 2;
|
|
|
|
if (minus) {
|
|
hour = -hour;
|
|
minute = -minute;
|
|
}
|
|
|
|
// Subtract the timezone offset.
|
|
if (!addMinute(-(hour * 60 + minute)))
|
|
return false;
|
|
end = index;
|
|
return true;
|
|
}
|
|
|
|
bool DateComponents::parseMonth(const String& src, unsigned start, unsigned& end)
|
|
{
|
|
unsigned index;
|
|
if (!parseYear(src, start, index))
|
|
return false;
|
|
if (index >= src.length() || src[index] != '-')
|
|
return false;
|
|
++index;
|
|
|
|
int month;
|
|
if (!toInt(src, index, 2, month) || month < 1 || month > 12)
|
|
return false;
|
|
--month;
|
|
if (!withinHTMLDateLimits(m_year, month))
|
|
return false;
|
|
m_month = month;
|
|
end = index + 2;
|
|
m_type = Month;
|
|
return true;
|
|
}
|
|
|
|
bool DateComponents::parseDate(const String& src, unsigned start, unsigned& end)
|
|
{
|
|
unsigned index;
|
|
if (!parseMonth(src, start, index))
|
|
return false;
|
|
// '-' and 2-digits are needed.
|
|
if (index + 2 >= src.length())
|
|
return false;
|
|
if (src[index] != '-')
|
|
return false;
|
|
++index;
|
|
|
|
int day;
|
|
if (!toInt(src, index, 2, day) || day < 1 || day > maxDayOfMonth(m_year, m_month))
|
|
return false;
|
|
if (!withinHTMLDateLimits(m_year, m_month, day))
|
|
return false;
|
|
m_monthDay = day;
|
|
end = index + 2;
|
|
m_type = Date;
|
|
return true;
|
|
}
|
|
|
|
bool DateComponents::parseWeek(const String& src, unsigned start, unsigned& end)
|
|
{
|
|
unsigned index;
|
|
if (!parseYear(src, start, index))
|
|
return false;
|
|
|
|
// 4 characters ('-' 'W' digit digit) are needed.
|
|
if (index + 3 >= src.length())
|
|
return false;
|
|
if (src[index] != '-')
|
|
return false;
|
|
++index;
|
|
if (src[index] != 'W')
|
|
return false;
|
|
++index;
|
|
|
|
int week;
|
|
if (!toInt(src, index, 2, week) || week < minimumWeekNumber || week > maxWeekNumberInYear())
|
|
return false;
|
|
if (m_year == maximumYear() && week > maximumWeekInMaximumYear)
|
|
return false;
|
|
m_week = week;
|
|
end = index + 2;
|
|
m_type = Week;
|
|
return true;
|
|
}
|
|
|
|
bool DateComponents::parseTime(const String& src, unsigned start, unsigned& end)
|
|
{
|
|
int hour;
|
|
if (!toInt(src, start, 2, hour) || hour < 0 || hour > 23)
|
|
return false;
|
|
unsigned index = start + 2;
|
|
if (index >= src.length())
|
|
return false;
|
|
if (src[index] != ':')
|
|
return false;
|
|
++index;
|
|
|
|
int minute;
|
|
if (!toInt(src, index, 2, minute) || minute < 0 || minute > 59)
|
|
return false;
|
|
index += 2;
|
|
|
|
int second = 0;
|
|
int millisecond = 0;
|
|
// Optional second part.
|
|
// Do not return with false because the part is optional.
|
|
if (index + 2 < src.length() && src[index] == ':') {
|
|
if (toInt(src, index + 1, 2, second) && second >= 0 && second <= 59) {
|
|
index += 3;
|
|
|
|
// Optional fractional second part.
|
|
if (index < src.length() && src[index] == '.') {
|
|
unsigned digitsLength = countDigits(src, index + 1);
|
|
if (digitsLength > 0) {
|
|
++index;
|
|
bool ok;
|
|
if (digitsLength == 1) {
|
|
ok = toInt(src, index, 1, millisecond);
|
|
millisecond *= 100;
|
|
} else if (digitsLength == 2) {
|
|
ok = toInt(src, index, 2, millisecond);
|
|
millisecond *= 10;
|
|
} else { // digitsLength >= 3
|
|
ok = toInt(src, index, 3, millisecond);
|
|
}
|
|
ASSERT_UNUSED(ok, ok);
|
|
index += digitsLength;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
m_hour = hour;
|
|
m_minute = minute;
|
|
m_second = second;
|
|
m_millisecond = millisecond;
|
|
end = index;
|
|
m_type = Time;
|
|
return true;
|
|
}
|
|
|
|
bool DateComponents::parseDateTimeLocal(const String& src, unsigned start, unsigned& end)
|
|
{
|
|
unsigned index;
|
|
if (!parseDate(src, start, index))
|
|
return false;
|
|
if (index >= src.length())
|
|
return false;
|
|
if (src[index] != 'T')
|
|
return false;
|
|
++index;
|
|
if (!parseTime(src, index, end))
|
|
return false;
|
|
if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond))
|
|
return false;
|
|
m_type = DateTimeLocal;
|
|
return true;
|
|
}
|
|
|
|
bool DateComponents::parseDateTime(const String& src, unsigned start, unsigned& end)
|
|
{
|
|
unsigned index;
|
|
if (!parseDate(src, start, index))
|
|
return false;
|
|
if (index >= src.length())
|
|
return false;
|
|
if (src[index] != 'T')
|
|
return false;
|
|
++index;
|
|
if (!parseTime(src, index, index))
|
|
return false;
|
|
if (!parseTimeZone(src, index, end))
|
|
return false;
|
|
if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond))
|
|
return false;
|
|
m_type = DateTime;
|
|
return true;
|
|
}
|
|
|
|
static inline double positiveFmod(double value, double divider)
|
|
{
|
|
double remainder = fmod(value, divider);
|
|
return remainder < 0 ? remainder + divider : remainder;
|
|
}
|
|
|
|
void DateComponents::setMillisecondsSinceMidnightInternal(double msInDay)
|
|
{
|
|
ASSERT(msInDay >= 0 && msInDay < msPerDay);
|
|
m_millisecond = static_cast<int>(fmod(msInDay, msPerSecond));
|
|
double value = std::floor(msInDay / msPerSecond);
|
|
m_second = static_cast<int>(fmod(value, secondsPerMinute));
|
|
value = std::floor(value / secondsPerMinute);
|
|
m_minute = static_cast<int>(fmod(value, minutesPerHour));
|
|
m_hour = static_cast<int>(value / minutesPerHour);
|
|
}
|
|
|
|
bool DateComponents::setMillisecondsSinceEpochForDateInternal(double ms)
|
|
{
|
|
m_year = msToYear(ms);
|
|
int yearDay = dayInYear(ms, m_year);
|
|
m_month = monthFromDayInYear(yearDay, isLeapYear(m_year));
|
|
m_monthDay = dayInMonthFromDayInYear(yearDay, isLeapYear(m_year));
|
|
return true;
|
|
}
|
|
|
|
bool DateComponents::setMillisecondsSinceEpochForDate(double ms)
|
|
{
|
|
m_type = Invalid;
|
|
if (!std::isfinite(ms))
|
|
return false;
|
|
if (!setMillisecondsSinceEpochForDateInternal(round(ms)))
|
|
return false;
|
|
if (!withinHTMLDateLimits(m_year, m_month, m_monthDay))
|
|
return false;
|
|
m_type = Date;
|
|
return true;
|
|
}
|
|
|
|
bool DateComponents::setMillisecondsSinceEpochForDateTime(double ms)
|
|
{
|
|
m_type = Invalid;
|
|
if (!std::isfinite(ms))
|
|
return false;
|
|
ms = round(ms);
|
|
setMillisecondsSinceMidnightInternal(positiveFmod(ms, msPerDay));
|
|
if (!setMillisecondsSinceEpochForDateInternal(ms))
|
|
return false;
|
|
if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond))
|
|
return false;
|
|
m_type = DateTime;
|
|
return true;
|
|
}
|
|
|
|
bool DateComponents::setMillisecondsSinceEpochForDateTimeLocal(double ms)
|
|
{
|
|
// Internal representation of DateTimeLocal is the same as DateTime except m_type.
|
|
if (!setMillisecondsSinceEpochForDateTime(ms))
|
|
return false;
|
|
m_type = DateTimeLocal;
|
|
return true;
|
|
}
|
|
|
|
bool DateComponents::setMillisecondsSinceEpochForMonth(double ms)
|
|
{
|
|
m_type = Invalid;
|
|
if (!std::isfinite(ms))
|
|
return false;
|
|
if (!setMillisecondsSinceEpochForDateInternal(round(ms)))
|
|
return false;
|
|
if (!withinHTMLDateLimits(m_year, m_month))
|
|
return false;
|
|
m_type = Month;
|
|
return true;
|
|
}
|
|
|
|
bool DateComponents::setMillisecondsSinceMidnight(double ms)
|
|
{
|
|
m_type = Invalid;
|
|
if (!std::isfinite(ms))
|
|
return false;
|
|
setMillisecondsSinceMidnightInternal(positiveFmod(round(ms), msPerDay));
|
|
m_type = Time;
|
|
return true;
|
|
}
|
|
|
|
bool DateComponents::setMonthsSinceEpoch(double months)
|
|
{
|
|
if (!std::isfinite(months))
|
|
return false;
|
|
months = round(months);
|
|
double doubleMonth = positiveFmod(months, 12);
|
|
double doubleYear = 1970 + (months - doubleMonth) / 12;
|
|
if (doubleYear < minimumYear() || maximumYear() < doubleYear)
|
|
return false;
|
|
int year = static_cast<int>(doubleYear);
|
|
int month = static_cast<int>(doubleMonth);
|
|
if (!withinHTMLDateLimits(year, month))
|
|
return false;
|
|
m_year = year;
|
|
m_month = month;
|
|
m_type = Month;
|
|
return true;
|
|
}
|
|
|
|
// Offset from January 1st to Monday of the ISO 8601's first week.
|
|
// ex. If January 1st is Friday, such Monday is 3 days later. Returns 3.
|
|
static int offsetTo1stWeekStart(int year)
|
|
{
|
|
int offsetTo1stWeekStart = 1 - dayOfWeek(year, 0, 1);
|
|
if (offsetTo1stWeekStart <= -4)
|
|
offsetTo1stWeekStart += 7;
|
|
return offsetTo1stWeekStart;
|
|
}
|
|
|
|
bool DateComponents::setMillisecondsSinceEpochForWeek(double ms)
|
|
{
|
|
m_type = Invalid;
|
|
if (!std::isfinite(ms))
|
|
return false;
|
|
ms = round(ms);
|
|
|
|
m_year = msToYear(ms);
|
|
if (m_year < minimumYear() || m_year > maximumYear())
|
|
return false;
|
|
|
|
int yearDay = dayInYear(ms, m_year);
|
|
int offset = offsetTo1stWeekStart(m_year);
|
|
if (yearDay < offset) {
|
|
// The day belongs to the last week of the previous year.
|
|
m_year--;
|
|
if (m_year <= minimumYear())
|
|
return false;
|
|
m_week = maxWeekNumberInYear();
|
|
} else {
|
|
m_week = ((yearDay - offset) / 7) + 1;
|
|
if (m_week > maxWeekNumberInYear()) {
|
|
m_year++;
|
|
m_week = 1;
|
|
}
|
|
if (m_year > maximumYear() || (m_year == maximumYear() && m_week > maximumWeekInMaximumYear))
|
|
return false;
|
|
}
|
|
m_type = Week;
|
|
return true;
|
|
}
|
|
|
|
double DateComponents::millisecondsSinceEpochForTime() const
|
|
{
|
|
ASSERT(m_type == Time || m_type == DateTime || m_type == DateTimeLocal);
|
|
return ((m_hour * minutesPerHour + m_minute) * secondsPerMinute + m_second) * msPerSecond + m_millisecond;
|
|
}
|
|
|
|
double DateComponents::millisecondsSinceEpoch() const
|
|
{
|
|
switch (m_type) {
|
|
case Date:
|
|
return dateToDaysFrom1970(m_year, m_month, m_monthDay) * msPerDay;
|
|
case DateTime:
|
|
case DateTimeLocal:
|
|
return dateToDaysFrom1970(m_year, m_month, m_monthDay) * msPerDay + millisecondsSinceEpochForTime();
|
|
case Month:
|
|
return dateToDaysFrom1970(m_year, m_month, 1) * msPerDay;
|
|
case Time:
|
|
return millisecondsSinceEpochForTime();
|
|
case Week:
|
|
return (dateToDaysFrom1970(m_year, 0, 1) + offsetTo1stWeekStart(m_year) + (m_week - 1) * 7) * msPerDay;
|
|
case Invalid:
|
|
break;
|
|
}
|
|
ASSERT_NOT_REACHED();
|
|
return invalidMilliseconds();
|
|
}
|
|
|
|
double DateComponents::monthsSinceEpoch() const
|
|
{
|
|
ASSERT(m_type == Month);
|
|
return (m_year - 1970) * 12 + m_month;
|
|
}
|
|
|
|
String DateComponents::toStringForTime(SecondFormat format) const
|
|
{
|
|
ASSERT(m_type == DateTime || m_type == DateTimeLocal || m_type == Time);
|
|
SecondFormat effectiveFormat = format;
|
|
if (m_millisecond)
|
|
effectiveFormat = Millisecond;
|
|
else if (format == None && m_second)
|
|
effectiveFormat = Second;
|
|
|
|
switch (effectiveFormat) {
|
|
default:
|
|
ASSERT_NOT_REACHED();
|
|
// Fallback to None.
|
|
case None:
|
|
return String::format("%02d:%02d", m_hour, m_minute);
|
|
case Second:
|
|
return String::format("%02d:%02d:%02d", m_hour, m_minute, m_second);
|
|
case Millisecond:
|
|
return String::format("%02d:%02d:%02d.%03d", m_hour, m_minute, m_second, m_millisecond);
|
|
}
|
|
}
|
|
|
|
String DateComponents::toString(SecondFormat format) const
|
|
{
|
|
switch (m_type) {
|
|
case Date:
|
|
return String::format("%04d-%02d-%02d", m_year, m_month + 1, m_monthDay);
|
|
case DateTime:
|
|
return String::format("%04d-%02d-%02dT", m_year, m_month + 1, m_monthDay)
|
|
+ toStringForTime(format) + String("Z");
|
|
case DateTimeLocal:
|
|
return String::format("%04d-%02d-%02dT", m_year, m_month + 1, m_monthDay)
|
|
+ toStringForTime(format);
|
|
case Month:
|
|
return String::format("%04d-%02d", m_year, m_month + 1);
|
|
case Time:
|
|
return toStringForTime(format);
|
|
case Week:
|
|
return String::format("%04d-W%02d", m_year, m_week);
|
|
case Invalid:
|
|
break;
|
|
}
|
|
ASSERT_NOT_REACHED();
|
|
return String("(Invalid DateComponents)");
|
|
}
|
|
|
|
} // namespace blink
|