• 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 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.
maxDayOfMonth(int year,int month)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.
dayOfWeek(int year,int month,int day)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 
weekDay() const82 int DateComponents::weekDay() const
83 {
84     return dayOfWeek(m_year, m_month, m_monthDay);
85 }
86 
maxWeekNumberInYear() const87 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 
countDigits(const String & src,unsigned start)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().
toInt(const String & src,unsigned parseStart,unsigned parseLength,int & out)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 
parseYear(const String & src,unsigned start,unsigned & end)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 
withinHTMLDateLimits(int year,int month)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 
withinHTMLDateLimits(int year,int month,int monthDay)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 
withinHTMLDateLimits(int year,int month,int monthDay,int hour,int minute,int second,int millisecond)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 
addDay(int dayDiff)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 
addMinute(int minute)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.
parseTimeZone(const String & src,unsigned start,unsigned & end)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 
parseMonth(const String & src,unsigned start,unsigned & end)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 
parseDate(const String & src,unsigned start,unsigned & end)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 
parseWeek(const String & src,unsigned start,unsigned & end)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 
parseTime(const String & src,unsigned start,unsigned & end)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 
parseDateTimeLocal(const String & src,unsigned start,unsigned & end)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 
parseDateTime(const String & src,unsigned start,unsigned & end)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 
positiveFmod(double value,double divider)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 
setMillisecondsSinceMidnightInternal(double msInDay)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 
setMillisecondsSinceEpochForDateInternal(double ms)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 
setMillisecondsSinceEpochForDate(double ms)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 
setMillisecondsSinceEpochForDateTime(double ms)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 
setMillisecondsSinceEpochForDateTimeLocal(double ms)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 
setMillisecondsSinceEpochForMonth(double ms)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 
setMillisecondsSinceMidnight(double ms)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 
setMonthsSinceEpoch(double months)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.
offsetTo1stWeekStart(int year)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 
setMillisecondsSinceEpochForWeek(double ms)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 
setWeek(int year,int weekNumber)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 
millisecondsSinceEpochForTime() const649 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 
millisecondsSinceEpoch() const655 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 
monthsSinceEpoch() const676 double DateComponents::monthsSinceEpoch() const
677 {
678     ASSERT(m_type == Month);
679     return (m_year - 1970) * 12 + m_month;
680 }
681 
toStringForTime(SecondFormat format) const682 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 
toString(SecondFormat format) const704 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