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