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