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