Home | History | Annotate | Download | only in html
      1 /*
      2  * Copyright (C) 2009 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "DateComponents.h"
     33 
     34 #include "PlatformString.h"
     35 #include <limits.h>
     36 #include <wtf/ASCIICType.h>
     37 #include <wtf/DateMath.h>
     38 #include <wtf/MathExtras.h>
     39 
     40 using namespace std;
     41 
     42 namespace WebCore {
     43 
     44 // HTML5 uses ISO-8601 format with year >= 1. Gregorian calendar started in
     45 // 1582. However, we need to support 0001-01-01 in Gregorian calendar rule.
     46 static const int minimumYear = 1;
     47 // Date in ECMAScript can't represent dates later than 275760-09-13T00:00Z.
     48 // So, we have the same upper limit in HTML5 dates.
     49 static const int maximumYear = 275760;
     50 static const int maximumMonthInMaximumYear = 8; // This is September, since months are 0 based.
     51 static const int maximumDayInMaximumMonth = 13;
     52 static const int maximumWeekInMaximumYear = 37; // The week of 275760-09-13
     53 
     54 static const int daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
     55 
     56 static bool isLeapYear(int year)
     57 {
     58     if (year % 4)
     59         return false;
     60     if (!(year % 400))
     61         return true;
     62     if (!(year % 100))
     63         return false;
     64     return true;
     65 }
     66 
     67 // 'month' is 0-based.
     68 static int maxDayOfMonth(int year, int month)
     69 {
     70     if (month != 1) // February?
     71         return daysInMonth[month];
     72     return isLeapYear(year) ? 29 : 28;
     73 }
     74 
     75 // 'month' is 0-based.
     76 static int dayOfWeek(int year, int month, int day)
     77 {
     78     int shiftedMonth = month + 2;
     79     // 2:January, 3:Feburuary, 4:March, ...
     80 
     81     // Zeller's congruence
     82     if (shiftedMonth <= 3) {
     83         shiftedMonth += 12;
     84         year--;
     85     }
     86     // 4:March, ..., 14:January, 15:February
     87 
     88     int highYear = year / 100;
     89     int lowYear = year % 100;
     90     // We add 6 to make the result Sunday-origin.
     91     int result = (day + 13 * shiftedMonth / 5 + lowYear + lowYear / 4 + highYear / 4 + 5 * highYear + 6) % 7;
     92     return result;
     93 }
     94 
     95 int DateComponents::maxWeekNumberInYear() const
     96 {
     97     int day = dayOfWeek(m_year, 0, 1); // January 1.
     98     return day == Thursday || (day == Wednesday && isLeapYear(m_year)) ? 53 : 52;
     99 }
    100 
    101 static unsigned countDigits(const UChar* src, unsigned length, unsigned start)
    102 {
    103     unsigned index = start;
    104     for (; index < length; ++index) {
    105         if (!isASCIIDigit(src[index]))
    106             break;
    107     }
    108     return index - start;
    109 }
    110 
    111 // Very strict integer parser. Do not allow leading or trailing whitespace unlike charactersToIntStrict().
    112 static bool toInt(const UChar* src, unsigned length, unsigned parseStart, unsigned parseLength, int& out)
    113 {
    114     if (parseStart + parseLength > length || parseLength <= 0)
    115         return false;
    116     int value = 0;
    117     const UChar* current = src + parseStart;
    118     const UChar* end = current + parseLength;
    119 
    120     // We don't need to handle negative numbers for ISO 8601.
    121     for (; current < end; ++current) {
    122         if (!isASCIIDigit(*current))
    123             return false;
    124         int digit = *current - '0';
    125         if (value > (INT_MAX - digit) / 10) // Check for overflow.
    126             return false;
    127         value = value * 10 + digit;
    128     }
    129     out = value;
    130     return true;
    131 }
    132 
    133 bool DateComponents::parseYear(const UChar* src, unsigned length, unsigned start, unsigned& end)
    134 {
    135     unsigned digitsLength = countDigits(src, length, start);
    136     // Needs at least 4 digits according to the standard.
    137     if (digitsLength < 4)
    138         return false;
    139     int year;
    140     if (!toInt(src, length, start, digitsLength, year))
    141         return false;
    142     if (year < minimumYear || year > maximumYear)
    143         return false;
    144     m_year = year;
    145     end = start + digitsLength;
    146     return true;
    147 }
    148 
    149 static bool withinHTMLDateLimits(int year, int month)
    150 {
    151     if (year < minimumYear)
    152         return false;
    153     if (year < maximumYear)
    154         return true;
    155     return month <= maximumMonthInMaximumYear;
    156 }
    157 
    158 static bool withinHTMLDateLimits(int year, int month, int monthDay)
    159 {
    160     if (year < minimumYear)
    161         return false;
    162     if (year < maximumYear)
    163         return true;
    164     if (month < maximumMonthInMaximumYear)
    165         return true;
    166     return monthDay <= maximumDayInMaximumMonth;
    167 }
    168 
    169 static bool withinHTMLDateLimits(int year, int month, int monthDay, int hour, int minute, int second, int millisecond)
    170 {
    171     if (year < minimumYear)
    172         return false;
    173     if (year < maximumYear)
    174         return true;
    175     if (month < maximumMonthInMaximumYear)
    176         return true;
    177     if (monthDay < maximumDayInMaximumMonth)
    178         return true;
    179     if (monthDay > maximumDayInMaximumMonth)
    180         return false;
    181     // (year, month, monthDay) = (maximumYear, maximumMonthInMaximumYear, maximumDayInMaximumMonth)
    182     return !hour && !minute && !second && !millisecond;
    183 }
    184 
    185 bool DateComponents::addDay(int dayDiff)
    186 {
    187     ASSERT(m_monthDay);
    188 
    189     int day = m_monthDay + dayDiff;
    190     if (day > maxDayOfMonth(m_year, m_month)) {
    191         day = m_monthDay;
    192         int year = m_year;
    193         int month = m_month;
    194         int maxDay = maxDayOfMonth(year, month);
    195         for (; dayDiff > 0; --dayDiff) {
    196             ++day;
    197             if (day > maxDay) {
    198                 day = 1;
    199                 ++month;
    200                 if (month >= 12) { // month is 0-origin.
    201                     month = 0;
    202                     ++year;
    203                 }
    204                 maxDay = maxDayOfMonth(year, month);
    205             }
    206         }
    207         if (!withinHTMLDateLimits(year, month, day))
    208             return false;
    209         m_year = year;
    210         m_month = month;
    211     } else if (day < 1) {
    212         int month = m_month;
    213         int year = m_year;
    214         day = m_monthDay;
    215         for (; dayDiff < 0; ++dayDiff) {
    216             --day;
    217             if (day < 1) {
    218                 --month;
    219                 if (month < 0) {
    220                     month = 11;
    221                     --year;
    222                 }
    223                 day = maxDayOfMonth(year, month);
    224             }
    225         }
    226         if (!withinHTMLDateLimits(year, month, day))
    227             return false;
    228         m_year = year;
    229         m_month = month;
    230     } else {
    231         if (!withinHTMLDateLimits(m_year, m_month, day))
    232             return false;
    233     }
    234     m_monthDay = day;
    235     return true;
    236 }
    237 
    238 bool DateComponents::addMinute(int minute)
    239 {
    240     // This function is used to adjust timezone offset. So m_year, m_month,
    241     // m_monthDay have values between the lower and higher limits.
    242     ASSERT(withinHTMLDateLimits(m_year, m_month, m_monthDay));
    243 
    244     int carry;
    245     // minute can be negative or greater than 59.
    246     minute += m_minute;
    247     if (minute > 59) {
    248         carry = minute / 60;
    249         minute = minute % 60;
    250     } else if (m_minute < 0) {
    251         carry = (59 - m_minute) / 60;
    252         minute += carry * 60;
    253         carry = -carry;
    254         ASSERT(minute >= 0 && minute <= 59);
    255     } else {
    256         if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, minute, m_second, m_millisecond))
    257             return false;
    258         m_minute = minute;
    259         return true;
    260     }
    261 
    262     int hour = m_hour + carry;
    263     if (hour > 23) {
    264         carry = hour / 24;
    265         hour = hour % 24;
    266     } else if (hour < 0) {
    267         carry = (23 - hour) / 24;
    268         hour += carry * 24;
    269         carry = -carry;
    270         ASSERT(hour >= 0 && hour <= 23);
    271     } else {
    272         if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, hour, minute, m_second, m_millisecond))
    273             return false;
    274         m_minute = minute;
    275         m_hour = hour;
    276         return true;
    277     }
    278     if (!addDay(carry))
    279         return false;
    280     if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, hour, minute, m_second, m_millisecond))
    281         return false;
    282     m_minute = minute;
    283     m_hour = hour;
    284     return true;
    285 }
    286 
    287 // Parses a timezone part, and adjust year, month, monthDay, hour, minute, second, millisecond.
    288 bool DateComponents::parseTimeZone(const UChar* src, unsigned length, unsigned start, unsigned& end)
    289 {
    290     if (start >= length)
    291         return false;
    292     unsigned index = start;
    293     if (src[index] == 'Z') {
    294         end = index + 1;
    295         return true;
    296     }
    297 
    298     bool minus;
    299     if (src[index] == '+')
    300         minus = false;
    301     else if (src[index] == '-')
    302         minus = true;
    303     else
    304         return false;
    305     ++index;
    306 
    307     int hour;
    308     int minute;
    309     if (!toInt(src, length, index, 2, hour) || hour < 0 || hour > 23)
    310         return false;
    311     index += 2;
    312 
    313     if (index >= length || src[index] != ':')
    314         return false;
    315     ++index;
    316 
    317     if (!toInt(src, length, index, 2, minute) || minute < 0 || minute > 59)
    318         return false;
    319     index += 2;
    320 
    321     if (minus) {
    322         hour = -hour;
    323         minute = -minute;
    324     }
    325 
    326     // Subtract the timezone offset.
    327     if (!addMinute(-(hour * 60 + minute)))
    328         return false;
    329     end = index;
    330     return true;
    331 }
    332 
    333 bool DateComponents::parseMonth(const UChar* src, unsigned length, unsigned start, unsigned& end)
    334 {
    335     ASSERT(src);
    336     unsigned index;
    337     if (!parseYear(src, length, start, index))
    338         return false;
    339     if (index >= length || src[index] != '-')
    340         return false;
    341     ++index;
    342 
    343     int month;
    344     if (!toInt(src, length, index, 2, month) || month < 1 || month > 12)
    345         return false;
    346     --month;
    347     if (!withinHTMLDateLimits(m_year, month))
    348         return false;
    349     m_month = month;
    350     end = index + 2;
    351     m_type = Month;
    352     return true;
    353 }
    354 
    355 bool DateComponents::parseDate(const UChar* src, unsigned length, unsigned start, unsigned& end)
    356 {
    357     ASSERT(src);
    358     unsigned index;
    359     if (!parseMonth(src, length, start, index))
    360         return false;
    361     // '-' and 2-digits are needed.
    362     if (index + 2 >= length)
    363         return false;
    364     if (src[index] != '-')
    365         return false;
    366     ++index;
    367 
    368     int day;
    369     if (!toInt(src, length, index, 2, day) || day < 1 || day > maxDayOfMonth(m_year, m_month))
    370         return false;
    371     if (!withinHTMLDateLimits(m_year, m_month, day))
    372         return false;
    373     m_monthDay = day;
    374     end = index + 2;
    375     m_type = Date;
    376     return true;
    377 }
    378 
    379 bool DateComponents::parseWeek(const UChar* src, unsigned length, unsigned start, unsigned& end)
    380 {
    381     ASSERT(src);
    382     unsigned index;
    383     if (!parseYear(src, length, start, index))
    384         return false;
    385 
    386     // 4 characters ('-' 'W' digit digit) are needed.
    387     if (index + 3 >= length)
    388         return false;
    389     if (src[index] != '-')
    390         return false;
    391     ++index;
    392     if (src[index] != 'W')
    393         return false;
    394     ++index;
    395 
    396     int week;
    397     if (!toInt(src, length, index, 2, week) || week < 1 || week > maxWeekNumberInYear())
    398         return false;
    399     if (m_year == maximumYear && week > maximumWeekInMaximumYear)
    400         return false;
    401     m_week = week;
    402     end = index + 2;
    403     m_type = Week;
    404     return true;
    405 }
    406 
    407 bool DateComponents::parseTime(const UChar* src, unsigned length, unsigned start, unsigned& end)
    408 {
    409     ASSERT(src);
    410     int hour;
    411     if (!toInt(src, length, start, 2, hour) || hour < 0 || hour > 23)
    412         return false;
    413     unsigned index = start + 2;
    414     if (index >= length)
    415         return false;
    416     if (src[index] != ':')
    417         return false;
    418     ++index;
    419 
    420     int minute;
    421     if (!toInt(src, length, index, 2, minute) || minute < 0 || minute > 59)
    422         return false;
    423     index += 2;
    424 
    425     int second = 0;
    426     int millisecond = 0;
    427     // Optional second part.
    428     // Do not return with false because the part is optional.
    429     if (index + 2 < length && src[index] == ':') {
    430         if (toInt(src, length, index + 1, 2, second) && second >= 0 && second <= 59) {
    431             index += 3;
    432 
    433             // Optional fractional second part.
    434             if (index < length && src[index] == '.') {
    435                 unsigned digitsLength = countDigits(src, length, index + 1);
    436                 if (digitsLength >  0) {
    437                     ++index;
    438                     bool ok;
    439                     if (digitsLength == 1) {
    440                         ok = toInt(src, length, index, 1, millisecond);
    441                         millisecond *= 100;
    442                     } else if (digitsLength == 2) {
    443                         ok = toInt(src, length, index, 2, millisecond);
    444                         millisecond *= 10;
    445                     } else // digitsLength >= 3
    446                         ok = toInt(src, length, index, 3, millisecond);
    447                     ASSERT(ok);
    448                     index += digitsLength;
    449                 }
    450             }
    451         }
    452     }
    453     m_hour = hour;
    454     m_minute = minute;
    455     m_second = second;
    456     m_millisecond = millisecond;
    457     end = index;
    458     m_type = Time;
    459     return true;
    460 }
    461 
    462 bool DateComponents::parseDateTimeLocal(const UChar* src, unsigned length, unsigned start, unsigned& end)
    463 {
    464     ASSERT(src);
    465     unsigned index;
    466     if (!parseDate(src, length, start, index))
    467         return false;
    468     if (index >= length)
    469         return false;
    470     if (src[index] != 'T')
    471         return false;
    472     ++index;
    473     if (!parseTime(src, length, index, end))
    474         return false;
    475     if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond))
    476         return false;
    477     m_type = DateTimeLocal;
    478     return true;
    479 }
    480 
    481 bool DateComponents::parseDateTime(const UChar* src, unsigned length, unsigned start, unsigned& end)
    482 {
    483     ASSERT(src);
    484     unsigned index;
    485     if (!parseDate(src, length, start, index))
    486         return false;
    487     if (index >= length)
    488         return false;
    489     if (src[index] != 'T')
    490         return false;
    491     ++index;
    492     if (!parseTime(src, length, index, index))
    493         return false;
    494     if (!parseTimeZone(src, length, index, end))
    495         return false;
    496     if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond))
    497         return false;
    498     m_type = DateTime;
    499     return true;
    500 }
    501 
    502 static inline double positiveFmod(double value, double divider)
    503 {
    504     double remainder = fmod(value, divider);
    505     return remainder < 0 ? remainder + divider : remainder;
    506 }
    507 
    508 void DateComponents::setMillisecondsSinceMidnightInternal(double msInDay)
    509 {
    510     ASSERT(msInDay >= 0 && msInDay < msPerDay);
    511     m_millisecond = static_cast<int>(fmod(msInDay, msPerSecond));
    512     double value = floor(msInDay / msPerSecond);
    513     m_second = static_cast<int>(fmod(value, secondsPerMinute));
    514     value = floor(value / secondsPerMinute);
    515     m_minute = static_cast<int>(fmod(value, minutesPerHour));
    516     m_hour = static_cast<int>(value / minutesPerHour);
    517 }
    518 
    519 bool DateComponents::setMillisecondsSinceEpochForDateInternal(double ms)
    520 {
    521     m_year = msToYear(ms);
    522     int yearDay = dayInYear(ms, m_year);
    523     m_month = monthFromDayInYear(yearDay, isLeapYear(m_year));
    524     m_monthDay = dayInMonthFromDayInYear(yearDay, isLeapYear(m_year));
    525     return true;
    526 }
    527 
    528 bool DateComponents::setMillisecondsSinceEpochForDate(double ms)
    529 {
    530     m_type = Invalid;
    531     if (!isfinite(ms))
    532         return false;
    533     if (!setMillisecondsSinceEpochForDateInternal(round(ms)))
    534         return false;
    535     if (!withinHTMLDateLimits(m_year, m_month, m_monthDay))
    536         return false;
    537     m_type = Date;
    538     return true;
    539 }
    540 
    541 bool DateComponents::setMillisecondsSinceEpochForDateTime(double ms)
    542 {
    543     m_type = Invalid;
    544     if (!isfinite(ms))
    545         return false;
    546     ms = round(ms);
    547     setMillisecondsSinceMidnightInternal(positiveFmod(ms, msPerDay));
    548     if (!setMillisecondsSinceEpochForDateInternal(ms))
    549         return false;
    550     if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond))
    551         return false;
    552     m_type = DateTime;
    553     return true;
    554 }
    555 
    556 bool DateComponents::setMillisecondsSinceEpochForDateTimeLocal(double ms)
    557 {
    558     // Internal representation of DateTimeLocal is the same as DateTime except m_type.
    559     if (!setMillisecondsSinceEpochForDateTime(ms))
    560         return false;
    561     m_type = DateTimeLocal;
    562     return true;
    563 }
    564 
    565 bool DateComponents::setMillisecondsSinceEpochForMonth(double ms)
    566 {
    567     m_type = Invalid;
    568     if (!isfinite(ms))
    569         return false;
    570     if (!setMillisecondsSinceEpochForDateInternal(round(ms)))
    571         return false;
    572     if (!withinHTMLDateLimits(m_year, m_month))
    573         return false;
    574     m_type = Month;
    575     return true;
    576 }
    577 
    578 bool DateComponents::setMillisecondsSinceMidnight(double ms)
    579 {
    580     m_type = Invalid;
    581     if (!isfinite(ms))
    582         return false;
    583     setMillisecondsSinceMidnightInternal(positiveFmod(round(ms), msPerDay));
    584     m_type = Time;
    585     return true;
    586 }
    587 
    588 bool DateComponents::setMonthsSinceEpoch(double months)
    589 {
    590     if (!isfinite(months))
    591         return false;
    592     months = round(months);
    593     double doubleMonth = positiveFmod(months, 12);
    594     double doubleYear = 1970 + (months - doubleMonth) / 12;
    595     if (doubleYear < minimumYear || maximumYear < doubleYear)
    596         return false;
    597     int year = static_cast<int>(doubleYear);
    598     int month = static_cast<int>(doubleMonth);
    599     if (!withinHTMLDateLimits(year, month))
    600         return false;
    601     m_year = year;
    602     m_month = month;
    603     m_type = Month;
    604     return true;
    605 }
    606 
    607 // Offset from January 1st to Monday of the ISO 8601's first week.
    608 //   ex. If January 1st is Friday, such Monday is 3 days later. Returns 3.
    609 static int offsetTo1stWeekStart(int year)
    610 {
    611     int offsetTo1stWeekStart = 1 - dayOfWeek(year, 0, 1);
    612     if (offsetTo1stWeekStart <= -4)
    613         offsetTo1stWeekStart += 7;
    614     return offsetTo1stWeekStart;
    615 }
    616 
    617 bool DateComponents::setMillisecondsSinceEpochForWeek(double ms)
    618 {
    619     m_type = Invalid;
    620     if (!isfinite(ms))
    621         return false;
    622     ms = round(ms);
    623 
    624     m_year = msToYear(ms);
    625     if (m_year < minimumYear || m_year > maximumYear)
    626         return false;
    627 
    628     int yearDay = dayInYear(ms, m_year);
    629     int offset = offsetTo1stWeekStart(m_year);
    630     if (yearDay < offset) {
    631         // The day belongs to the last week of the previous year.
    632         m_year--;
    633         if (m_year <= minimumYear)
    634             return false;
    635         m_week = maxWeekNumberInYear();
    636     } else {
    637         m_week = ((yearDay - offset) / 7) + 1;
    638         if (m_week > maxWeekNumberInYear()) {
    639             m_year++;
    640             m_week = 1;
    641         }
    642         if (m_year > maximumYear || (m_year == maximumYear && m_week > maximumWeekInMaximumYear))
    643             return false;
    644     }
    645     m_type = Week;
    646     return true;
    647 }
    648 
    649 double DateComponents::millisecondsSinceEpochForTime() const
    650 {
    651     ASSERT(m_type == Time || m_type == DateTime || m_type == DateTimeLocal);
    652     return ((m_hour * minutesPerHour + m_minute) * secondsPerMinute + m_second) * msPerSecond + m_millisecond;
    653 }
    654 
    655 double DateComponents::millisecondsSinceEpoch() const
    656 {
    657     switch (m_type) {
    658     case Date:
    659         return dateToDaysFrom1970(m_year, m_month, m_monthDay) * msPerDay;
    660     case DateTime:
    661     case DateTimeLocal:
    662         return dateToDaysFrom1970(m_year, m_month, m_monthDay) * msPerDay + millisecondsSinceEpochForTime();
    663     case Month:
    664         return dateToDaysFrom1970(m_year, m_month, 1) * msPerDay;
    665     case Time:
    666         return millisecondsSinceEpochForTime();
    667     case Week:
    668         return (dateToDaysFrom1970(m_year, 0, 1) + offsetTo1stWeekStart(m_year) + (m_week - 1) * 7) * msPerDay;
    669     case Invalid:
    670         break;
    671     }
    672     ASSERT_NOT_REACHED();
    673     return invalidMilliseconds();
    674 }
    675 
    676 double DateComponents::monthsSinceEpoch() const
    677 {
    678     ASSERT(m_type == Month);
    679     return (m_year - 1970) * 12 + m_month;
    680 }
    681 
    682 String DateComponents::toStringForTime(SecondFormat format) const
    683 {
    684     ASSERT(m_type == DateTime || m_type == DateTimeLocal || m_type == Time);
    685     SecondFormat effectiveFormat = format;
    686     if (m_millisecond)
    687         effectiveFormat = Millisecond;
    688     else if (format == None && m_second)
    689         effectiveFormat = Second;
    690 
    691     switch (effectiveFormat) {
    692     default:
    693         ASSERT_NOT_REACHED();
    694         // Fallback to None.
    695     case None:
    696         return String::format("%02d:%02d", m_hour, m_minute);
    697     case Second:
    698         return String::format("%02d:%02d:%02d", m_hour, m_minute, m_second);
    699     case Millisecond:
    700         return String::format("%02d:%02d:%02d.%03d", m_hour, m_minute, m_second, m_millisecond);
    701     }
    702 }
    703 
    704 String DateComponents::toString(SecondFormat format) const
    705 {
    706     switch (m_type) {
    707     case Date:
    708         return String::format("%04d-%02d-%02d", m_year, m_month + 1, m_monthDay);
    709     case DateTime:
    710         return String::format("%04d-%02d-%02dT", m_year, m_month + 1, m_monthDay)
    711             + toStringForTime(format) + String("Z");
    712     case DateTimeLocal:
    713         return String::format("%04d-%02d-%02dT", m_year, m_month + 1, m_monthDay)
    714             + toStringForTime(format);
    715     case Month:
    716         return String::format("%04d-%02d", m_year, m_month + 1);
    717     case Time:
    718         return toStringForTime(format);
    719     case Week:
    720         return String::format("%04d-W%02d", m_year, m_week);
    721     case Invalid:
    722         break;
    723     }
    724     ASSERT_NOT_REACHED();
    725     return String("(Invalid DateComponents)");
    726 }
    727 
    728 } // namespace WebCore
    729