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 "core/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 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)) ? maximumWeekNumber : maximumWeekNumber - 1;
     99 }
    100 
    101 static unsigned countDigits(const String& src, unsigned start)
    102 {
    103     unsigned index = start;
    104     for (; index < src.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 String& src, unsigned parseStart, unsigned parseLength, int& out)
    113 {
    114     if (parseStart + parseLength > src.length() || parseLength <= 0)
    115         return false;
    116     int value = 0;
    117     unsigned current = parseStart;
    118     unsigned end = current + parseLength;
    119 
    120     // We don't need to handle negative numbers for ISO 8601.
    121     for (; current < end; ++current) {
    122         if (!isASCIIDigit(src[current]))
    123             return false;
    124         int digit = src[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 String& src, unsigned start, unsigned& end)
    134 {
    135     unsigned digitsLength = countDigits(src, 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, 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 < DateComponents::minimumYear())
    152         return false;
    153     if (year < DateComponents::maximumYear())
    154         return true;
    155     return month <= maximumMonthInMaximumYear;
    156 }
    157 
    158 static bool withinHTMLDateLimits(int year, int month, int monthDay)
    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     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 < DateComponents::minimumYear())
    172         return false;
    173     if (year < DateComponents::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 (minute < 0) {
    251         carry = (59 - 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 String& src, unsigned start, unsigned& end)
    289 {
    290     if (start >= src.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, index, 2, hour) || hour < 0 || hour > 23)
    310         return false;
    311     index += 2;
    312 
    313     if (index >= src.length() || src[index] != ':')
    314         return false;
    315     ++index;
    316 
    317     if (!toInt(src, 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 String& src, unsigned start, unsigned& end)
    334 {
    335     unsigned index;
    336     if (!parseYear(src, start, index))
    337         return false;
    338     if (index >= src.length() || src[index] != '-')
    339         return false;
    340     ++index;
    341 
    342     int month;
    343     if (!toInt(src, index, 2, month) || month < 1 || month > 12)
    344         return false;
    345     --month;
    346     if (!withinHTMLDateLimits(m_year, month))
    347         return false;
    348     m_month = month;
    349     end = index + 2;
    350     m_type = Month;
    351     return true;
    352 }
    353 
    354 bool DateComponents::parseDate(const String& src, unsigned start, unsigned& end)
    355 {
    356     unsigned index;
    357     if (!parseMonth(src, start, index))
    358         return false;
    359     // '-' and 2-digits are needed.
    360     if (index + 2 >= src.length())
    361         return false;
    362     if (src[index] != '-')
    363         return false;
    364     ++index;
    365 
    366     int day;
    367     if (!toInt(src, index, 2, day) || day < 1 || day > maxDayOfMonth(m_year, m_month))
    368         return false;
    369     if (!withinHTMLDateLimits(m_year, m_month, day))
    370         return false;
    371     m_monthDay = day;
    372     end = index + 2;
    373     m_type = Date;
    374     return true;
    375 }
    376 
    377 bool DateComponents::parseWeek(const String& src, unsigned start, unsigned& end)
    378 {
    379     unsigned index;
    380     if (!parseYear(src, start, index))
    381         return false;
    382 
    383     // 4 characters ('-' 'W' digit digit) are needed.
    384     if (index + 3 >= src.length())
    385         return false;
    386     if (src[index] != '-')
    387         return false;
    388     ++index;
    389     if (src[index] != 'W')
    390         return false;
    391     ++index;
    392 
    393     int week;
    394     if (!toInt(src, index, 2, week) || week < minimumWeekNumber || week > maxWeekNumberInYear())
    395         return false;
    396     if (m_year == maximumYear() && week > maximumWeekInMaximumYear)
    397         return false;
    398     m_week = week;
    399     end = index + 2;
    400     m_type = Week;
    401     return true;
    402 }
    403 
    404 bool DateComponents::parseTime(const String& src, unsigned start, unsigned& end)
    405 {
    406     int hour;
    407     if (!toInt(src, start, 2, hour) || hour < 0 || hour > 23)
    408         return false;
    409     unsigned index = start + 2;
    410     if (index >= src.length())
    411         return false;
    412     if (src[index] != ':')
    413         return false;
    414     ++index;
    415 
    416     int minute;
    417     if (!toInt(src, index, 2, minute) || minute < 0 || minute > 59)
    418         return false;
    419     index += 2;
    420 
    421     int second = 0;
    422     int millisecond = 0;
    423     // Optional second part.
    424     // Do not return with false because the part is optional.
    425     if (index + 2 < src.length() && src[index] == ':') {
    426         if (toInt(src, index + 1, 2, second) && second >= 0 && second <= 59) {
    427             index += 3;
    428 
    429             // Optional fractional second part.
    430             if (index < src.length() && src[index] == '.') {
    431                 unsigned digitsLength = countDigits(src, index + 1);
    432                 if (digitsLength >  0) {
    433                     ++index;
    434                     bool ok;
    435                     if (digitsLength == 1) {
    436                         ok = toInt(src, index, 1, millisecond);
    437                         millisecond *= 100;
    438                     } else if (digitsLength == 2) {
    439                         ok = toInt(src, index, 2, millisecond);
    440                         millisecond *= 10;
    441                     } else { // digitsLength >= 3
    442                         ok = toInt(src, index, 3, millisecond);
    443                     }
    444                     ASSERT_UNUSED(ok, ok);
    445                     index += digitsLength;
    446                 }
    447             }
    448         }
    449     }
    450     m_hour = hour;
    451     m_minute = minute;
    452     m_second = second;
    453     m_millisecond = millisecond;
    454     end = index;
    455     m_type = Time;
    456     return true;
    457 }
    458 
    459 bool DateComponents::parseDateTimeLocal(const String& src, unsigned start, unsigned& end)
    460 {
    461     unsigned index;
    462     if (!parseDate(src, start, index))
    463         return false;
    464     if (index >= src.length())
    465         return false;
    466     if (src[index] != 'T')
    467         return false;
    468     ++index;
    469     if (!parseTime(src, index, end))
    470         return false;
    471     if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond))
    472         return false;
    473     m_type = DateTimeLocal;
    474     return true;
    475 }
    476 
    477 bool DateComponents::parseDateTime(const String& src, unsigned start, unsigned& end)
    478 {
    479     unsigned index;
    480     if (!parseDate(src, start, index))
    481         return false;
    482     if (index >= src.length())
    483         return false;
    484     if (src[index] != 'T')
    485         return false;
    486     ++index;
    487     if (!parseTime(src, index, index))
    488         return false;
    489     if (!parseTimeZone(src, index, end))
    490         return false;
    491     if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond))
    492         return false;
    493     m_type = DateTime;
    494     return true;
    495 }
    496 
    497 static inline double positiveFmod(double value, double divider)
    498 {
    499     double remainder = fmod(value, divider);
    500     return remainder < 0 ? remainder + divider : remainder;
    501 }
    502 
    503 void DateComponents::setMillisecondsSinceMidnightInternal(double msInDay)
    504 {
    505     ASSERT(msInDay >= 0 && msInDay < msPerDay);
    506     m_millisecond = static_cast<int>(fmod(msInDay, msPerSecond));
    507     double value = floor(msInDay / msPerSecond);
    508     m_second = static_cast<int>(fmod(value, secondsPerMinute));
    509     value = floor(value / secondsPerMinute);
    510     m_minute = static_cast<int>(fmod(value, minutesPerHour));
    511     m_hour = static_cast<int>(value / minutesPerHour);
    512 }
    513 
    514 bool DateComponents::setMillisecondsSinceEpochForDateInternal(double ms)
    515 {
    516     m_year = msToYear(ms);
    517     int yearDay = dayInYear(ms, m_year);
    518     m_month = monthFromDayInYear(yearDay, isLeapYear(m_year));
    519     m_monthDay = dayInMonthFromDayInYear(yearDay, isLeapYear(m_year));
    520     return true;
    521 }
    522 
    523 bool DateComponents::setMillisecondsSinceEpochForDate(double ms)
    524 {
    525     m_type = Invalid;
    526     if (!std::isfinite(ms))
    527         return false;
    528     if (!setMillisecondsSinceEpochForDateInternal(round(ms)))
    529         return false;
    530     if (!withinHTMLDateLimits(m_year, m_month, m_monthDay))
    531         return false;
    532     m_type = Date;
    533     return true;
    534 }
    535 
    536 bool DateComponents::setMillisecondsSinceEpochForDateTime(double ms)
    537 {
    538     m_type = Invalid;
    539     if (!std::isfinite(ms))
    540         return false;
    541     ms = round(ms);
    542     setMillisecondsSinceMidnightInternal(positiveFmod(ms, msPerDay));
    543     if (!setMillisecondsSinceEpochForDateInternal(ms))
    544         return false;
    545     if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond))
    546         return false;
    547     m_type = DateTime;
    548     return true;
    549 }
    550 
    551 bool DateComponents::setMillisecondsSinceEpochForDateTimeLocal(double ms)
    552 {
    553     // Internal representation of DateTimeLocal is the same as DateTime except m_type.
    554     if (!setMillisecondsSinceEpochForDateTime(ms))
    555         return false;
    556     m_type = DateTimeLocal;
    557     return true;
    558 }
    559 
    560 bool DateComponents::setMillisecondsSinceEpochForMonth(double ms)
    561 {
    562     m_type = Invalid;
    563     if (!std::isfinite(ms))
    564         return false;
    565     if (!setMillisecondsSinceEpochForDateInternal(round(ms)))
    566         return false;
    567     if (!withinHTMLDateLimits(m_year, m_month))
    568         return false;
    569     m_type = Month;
    570     return true;
    571 }
    572 
    573 bool DateComponents::setMillisecondsSinceMidnight(double ms)
    574 {
    575     m_type = Invalid;
    576     if (!std::isfinite(ms))
    577         return false;
    578     setMillisecondsSinceMidnightInternal(positiveFmod(round(ms), msPerDay));
    579     m_type = Time;
    580     return true;
    581 }
    582 
    583 bool DateComponents::setMonthsSinceEpoch(double months)
    584 {
    585     if (!std::isfinite(months))
    586         return false;
    587     months = round(months);
    588     double doubleMonth = positiveFmod(months, 12);
    589     double doubleYear = 1970 + (months - doubleMonth) / 12;
    590     if (doubleYear < minimumYear() || maximumYear() < doubleYear)
    591         return false;
    592     int year = static_cast<int>(doubleYear);
    593     int month = static_cast<int>(doubleMonth);
    594     if (!withinHTMLDateLimits(year, month))
    595         return false;
    596     m_year = year;
    597     m_month = month;
    598     m_type = Month;
    599     return true;
    600 }
    601 
    602 // Offset from January 1st to Monday of the ISO 8601's first week.
    603 //   ex. If January 1st is Friday, such Monday is 3 days later. Returns 3.
    604 static int offsetTo1stWeekStart(int year)
    605 {
    606     int offsetTo1stWeekStart = 1 - dayOfWeek(year, 0, 1);
    607     if (offsetTo1stWeekStart <= -4)
    608         offsetTo1stWeekStart += 7;
    609     return offsetTo1stWeekStart;
    610 }
    611 
    612 bool DateComponents::setMillisecondsSinceEpochForWeek(double ms)
    613 {
    614     m_type = Invalid;
    615     if (!std::isfinite(ms))
    616         return false;
    617     ms = round(ms);
    618 
    619     m_year = msToYear(ms);
    620     if (m_year < minimumYear() || m_year > maximumYear())
    621         return false;
    622 
    623     int yearDay = dayInYear(ms, m_year);
    624     int offset = offsetTo1stWeekStart(m_year);
    625     if (yearDay < offset) {
    626         // The day belongs to the last week of the previous year.
    627         m_year--;
    628         if (m_year <= minimumYear())
    629             return false;
    630         m_week = maxWeekNumberInYear();
    631     } else {
    632         m_week = ((yearDay - offset) / 7) + 1;
    633         if (m_week > maxWeekNumberInYear()) {
    634             m_year++;
    635             m_week = 1;
    636         }
    637         if (m_year > maximumYear() || (m_year == maximumYear() && m_week > maximumWeekInMaximumYear))
    638             return false;
    639     }
    640     m_type = Week;
    641     return true;
    642 }
    643 
    644 double DateComponents::millisecondsSinceEpochForTime() const
    645 {
    646     ASSERT(m_type == Time || m_type == DateTime || m_type == DateTimeLocal);
    647     return ((m_hour * minutesPerHour + m_minute) * secondsPerMinute + m_second) * msPerSecond + m_millisecond;
    648 }
    649 
    650 double DateComponents::millisecondsSinceEpoch() const
    651 {
    652     switch (m_type) {
    653     case Date:
    654         return dateToDaysFrom1970(m_year, m_month, m_monthDay) * msPerDay;
    655     case DateTime:
    656     case DateTimeLocal:
    657         return dateToDaysFrom1970(m_year, m_month, m_monthDay) * msPerDay + millisecondsSinceEpochForTime();
    658     case Month:
    659         return dateToDaysFrom1970(m_year, m_month, 1) * msPerDay;
    660     case Time:
    661         return millisecondsSinceEpochForTime();
    662     case Week:
    663         return (dateToDaysFrom1970(m_year, 0, 1) + offsetTo1stWeekStart(m_year) + (m_week - 1) * 7) * msPerDay;
    664     case Invalid:
    665         break;
    666     }
    667     ASSERT_NOT_REACHED();
    668     return invalidMilliseconds();
    669 }
    670 
    671 double DateComponents::monthsSinceEpoch() const
    672 {
    673     ASSERT(m_type == Month);
    674     return (m_year - 1970) * 12 + m_month;
    675 }
    676 
    677 String DateComponents::toStringForTime(SecondFormat format) const
    678 {
    679     ASSERT(m_type == DateTime || m_type == DateTimeLocal || m_type == Time);
    680     SecondFormat effectiveFormat = format;
    681     if (m_millisecond)
    682         effectiveFormat = Millisecond;
    683     else if (format == None && m_second)
    684         effectiveFormat = Second;
    685 
    686     switch (effectiveFormat) {
    687     default:
    688         ASSERT_NOT_REACHED();
    689         // Fallback to None.
    690     case None:
    691         return String::format("%02d:%02d", m_hour, m_minute);
    692     case Second:
    693         return String::format("%02d:%02d:%02d", m_hour, m_minute, m_second);
    694     case Millisecond:
    695         return String::format("%02d:%02d:%02d.%03d", m_hour, m_minute, m_second, m_millisecond);
    696     }
    697 }
    698 
    699 String DateComponents::toString(SecondFormat format) const
    700 {
    701     switch (m_type) {
    702     case Date:
    703         return String::format("%04d-%02d-%02d", m_year, m_month + 1, m_monthDay);
    704     case DateTime:
    705         return String::format("%04d-%02d-%02dT", m_year, m_month + 1, m_monthDay)
    706             + toStringForTime(format) + String("Z");
    707     case DateTimeLocal:
    708         return String::format("%04d-%02d-%02dT", m_year, m_month + 1, m_monthDay)
    709             + toStringForTime(format);
    710     case Month:
    711         return String::format("%04d-%02d", m_year, m_month + 1);
    712     case Time:
    713         return toStringForTime(format);
    714     case Week:
    715         return String::format("%04d-W%02d", m_year, m_week);
    716     case Invalid:
    717         break;
    718     }
    719     ASSERT_NOT_REACHED();
    720     return String("(Invalid DateComponents)");
    721 }
    722 
    723 } // namespace WebCore
    724