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