• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
maxDayOfMonth(int year,int month)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.
dayOfWeek(int year,int month,int day)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 
maxWeekNumberInYear() const84 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 
countDigits(const String & src,unsigned start)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().
toInt(const String & src,unsigned parseStart,unsigned parseLength,int & out)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 
parseYear(const String & src,unsigned start,unsigned & end)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 
withinHTMLDateLimits(int year,int month)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 
withinHTMLDateLimits(int year,int month,int monthDay)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 
withinHTMLDateLimits(int year,int month,int monthDay,int hour,int minute,int second,int millisecond)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 
addDay(int dayDiff)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 
addMinute(int minute)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.
parseTimeZone(const String & src,unsigned start,unsigned & end)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 
parseMonth(const String & src,unsigned start,unsigned & end)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 
parseDate(const String & src,unsigned start,unsigned & end)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 
parseWeek(const String & src,unsigned start,unsigned & end)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 
parseTime(const String & src,unsigned start,unsigned & end)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 
parseDateTimeLocal(const String & src,unsigned start,unsigned & end)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 
parseDateTime(const String & src,unsigned start,unsigned & end)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 
positiveFmod(double value,double divider)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 
setMillisecondsSinceMidnightInternal(double msInDay)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 
setMillisecondsSinceEpochForDateInternal(double ms)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 
setMillisecondsSinceEpochForDate(double ms)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 
setMillisecondsSinceEpochForDateTime(double ms)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 
setMillisecondsSinceEpochForDateTimeLocal(double ms)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 
setMillisecondsSinceEpochForMonth(double ms)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 
setMillisecondsSinceMidnight(double ms)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 
setMonthsSinceEpoch(double months)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.
offsetTo1stWeekStart(int year)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 
setMillisecondsSinceEpochForWeek(double ms)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 
millisecondsSinceEpochForTime() const633 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 
millisecondsSinceEpoch() const639 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 
monthsSinceEpoch() const660 double DateComponents::monthsSinceEpoch() const
661 {
662     ASSERT(m_type == Month);
663     return (m_year - 1970) * 12 + m_month;
664 }
665 
toStringForTime(SecondFormat format) const666 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 
toString(SecondFormat format) const688 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