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