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