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 "DateComponents.h"
33
34 #include "PlatformString.h"
35 #include <limits.h>
36 #include <wtf/ASCIICType.h>
37 #include <wtf/DateMath.h>
38 #include <wtf/MathExtras.h>
39
40 using namespace std;
41
42 namespace WebCore {
43
44 // HTML5 uses ISO-8601 format with year >= 1. Gregorian calendar started in
45 // 1582. However, we need to support 0001-01-01 in Gregorian calendar rule.
46 static const int minimumYear = 1;
47 // Date in ECMAScript can't represent dates later than 275760-09-13T00:00Z.
48 // So, we have the same upper limit in HTML5 dates.
49 static const int maximumYear = 275760;
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
isLeapYear(int year)56 static bool isLeapYear(int year)
57 {
58 if (year % 4)
59 return false;
60 if (!(year % 400))
61 return true;
62 if (!(year % 100))
63 return false;
64 return true;
65 }
66
67 // 'month' is 0-based.
maxDayOfMonth(int year,int month)68 static int maxDayOfMonth(int year, int month)
69 {
70 if (month != 1) // February?
71 return daysInMonth[month];
72 return isLeapYear(year) ? 29 : 28;
73 }
74
75 // 'month' is 0-based.
dayOfWeek(int year,int month,int day)76 static int dayOfWeek(int year, int month, int day)
77 {
78 int shiftedMonth = month + 2;
79 // 2:January, 3:Feburuary, 4:March, ...
80
81 // Zeller's congruence
82 if (shiftedMonth <= 3) {
83 shiftedMonth += 12;
84 year--;
85 }
86 // 4:March, ..., 14:January, 15:February
87
88 int highYear = year / 100;
89 int lowYear = year % 100;
90 // We add 6 to make the result Sunday-origin.
91 int result = (day + 13 * shiftedMonth / 5 + lowYear + lowYear / 4 + highYear / 4 + 5 * highYear + 6) % 7;
92 return result;
93 }
94
maxWeekNumberInYear() const95 int DateComponents::maxWeekNumberInYear() const
96 {
97 int day = dayOfWeek(m_year, 0, 1); // January 1.
98 return day == Thursday || (day == Wednesday && isLeapYear(m_year)) ? 53 : 52;
99 }
100
countDigits(const UChar * src,unsigned length,unsigned start)101 static unsigned countDigits(const UChar* src, unsigned length, unsigned start)
102 {
103 unsigned index = start;
104 for (; index < length; ++index) {
105 if (!isASCIIDigit(src[index]))
106 break;
107 }
108 return index - start;
109 }
110
111 // Very strict integer parser. Do not allow leading or trailing whitespace unlike charactersToIntStrict().
toInt(const UChar * src,unsigned length,unsigned parseStart,unsigned parseLength,int & out)112 static bool toInt(const UChar* src, unsigned length, unsigned parseStart, unsigned parseLength, int& out)
113 {
114 if (parseStart + parseLength > length || parseLength <= 0)
115 return false;
116 int value = 0;
117 const UChar* current = src + parseStart;
118 const UChar* end = current + parseLength;
119
120 // We don't need to handle negative numbers for ISO 8601.
121 for (; current < end; ++current) {
122 if (!isASCIIDigit(*current))
123 return false;
124 int digit = *current - '0';
125 if (value > (INT_MAX - digit) / 10) // Check for overflow.
126 return false;
127 value = value * 10 + digit;
128 }
129 out = value;
130 return true;
131 }
132
parseYear(const UChar * src,unsigned length,unsigned start,unsigned & end)133 bool DateComponents::parseYear(const UChar* src, unsigned length, unsigned start, unsigned& end)
134 {
135 unsigned digitsLength = countDigits(src, length, start);
136 // Needs at least 4 digits according to the standard.
137 if (digitsLength < 4)
138 return false;
139 int year;
140 if (!toInt(src, length, start, digitsLength, year))
141 return false;
142 if (year < minimumYear || year > maximumYear)
143 return false;
144 m_year = year;
145 end = start + digitsLength;
146 return true;
147 }
148
withinHTMLDateLimits(int year,int month)149 static bool withinHTMLDateLimits(int year, int month)
150 {
151 if (year < minimumYear)
152 return false;
153 if (year < maximumYear)
154 return true;
155 return month <= maximumMonthInMaximumYear;
156 }
157
withinHTMLDateLimits(int year,int month,int monthDay)158 static bool withinHTMLDateLimits(int year, int month, int monthDay)
159 {
160 if (year < minimumYear)
161 return false;
162 if (year < maximumYear)
163 return true;
164 if (month < maximumMonthInMaximumYear)
165 return true;
166 return monthDay <= maximumDayInMaximumMonth;
167 }
168
withinHTMLDateLimits(int year,int month,int monthDay,int hour,int minute,int second,int millisecond)169 static bool withinHTMLDateLimits(int year, int month, int monthDay, int hour, int minute, int second, int millisecond)
170 {
171 if (year < minimumYear)
172 return false;
173 if (year < maximumYear)
174 return true;
175 if (month < maximumMonthInMaximumYear)
176 return true;
177 if (monthDay < maximumDayInMaximumMonth)
178 return true;
179 if (monthDay > maximumDayInMaximumMonth)
180 return false;
181 // (year, month, monthDay) = (maximumYear, maximumMonthInMaximumYear, maximumDayInMaximumMonth)
182 return !hour && !minute && !second && !millisecond;
183 }
184
addDay(int dayDiff)185 bool DateComponents::addDay(int dayDiff)
186 {
187 ASSERT(m_monthDay);
188
189 int day = m_monthDay + dayDiff;
190 if (day > maxDayOfMonth(m_year, m_month)) {
191 day = m_monthDay;
192 int year = m_year;
193 int month = m_month;
194 int maxDay = maxDayOfMonth(year, month);
195 for (; dayDiff > 0; --dayDiff) {
196 ++day;
197 if (day > maxDay) {
198 day = 1;
199 ++month;
200 if (month >= 12) { // month is 0-origin.
201 month = 0;
202 ++year;
203 }
204 maxDay = maxDayOfMonth(year, month);
205 }
206 }
207 if (!withinHTMLDateLimits(year, month, day))
208 return false;
209 m_year = year;
210 m_month = month;
211 } else if (day < 1) {
212 int month = m_month;
213 int year = m_year;
214 day = m_monthDay;
215 for (; dayDiff < 0; ++dayDiff) {
216 --day;
217 if (day < 1) {
218 --month;
219 if (month < 0) {
220 month = 11;
221 --year;
222 }
223 day = maxDayOfMonth(year, month);
224 }
225 }
226 if (!withinHTMLDateLimits(year, month, day))
227 return false;
228 m_year = year;
229 m_month = month;
230 } else {
231 if (!withinHTMLDateLimits(m_year, m_month, day))
232 return false;
233 }
234 m_monthDay = day;
235 return true;
236 }
237
addMinute(int minute)238 bool DateComponents::addMinute(int minute)
239 {
240 // This function is used to adjust timezone offset. So m_year, m_month,
241 // m_monthDay have values between the lower and higher limits.
242 ASSERT(withinHTMLDateLimits(m_year, m_month, m_monthDay));
243
244 int carry;
245 // minute can be negative or greater than 59.
246 minute += m_minute;
247 if (minute > 59) {
248 carry = minute / 60;
249 minute = minute % 60;
250 } else if (m_minute < 0) {
251 carry = (59 - m_minute) / 60;
252 minute += carry * 60;
253 carry = -carry;
254 ASSERT(minute >= 0 && minute <= 59);
255 } else {
256 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, minute, m_second, m_millisecond))
257 return false;
258 m_minute = minute;
259 return true;
260 }
261
262 int hour = m_hour + carry;
263 if (hour > 23) {
264 carry = hour / 24;
265 hour = hour % 24;
266 } else if (hour < 0) {
267 carry = (23 - hour) / 24;
268 hour += carry * 24;
269 carry = -carry;
270 ASSERT(hour >= 0 && hour <= 23);
271 } else {
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 if (!addDay(carry))
279 return false;
280 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, hour, minute, m_second, m_millisecond))
281 return false;
282 m_minute = minute;
283 m_hour = hour;
284 return true;
285 }
286
287 // Parses a timezone part, and adjust year, month, monthDay, hour, minute, second, millisecond.
parseTimeZone(const UChar * src,unsigned length,unsigned start,unsigned & end)288 bool DateComponents::parseTimeZone(const UChar* src, unsigned length, unsigned start, unsigned& end)
289 {
290 if (start >= length)
291 return false;
292 unsigned index = start;
293 if (src[index] == 'Z') {
294 end = index + 1;
295 return true;
296 }
297
298 bool minus;
299 if (src[index] == '+')
300 minus = false;
301 else if (src[index] == '-')
302 minus = true;
303 else
304 return false;
305 ++index;
306
307 int hour;
308 int minute;
309 if (!toInt(src, length, index, 2, hour) || hour < 0 || hour > 23)
310 return false;
311 index += 2;
312
313 if (index >= length || src[index] != ':')
314 return false;
315 ++index;
316
317 if (!toInt(src, length, index, 2, minute) || minute < 0 || minute > 59)
318 return false;
319 index += 2;
320
321 if (minus) {
322 hour = -hour;
323 minute = -minute;
324 }
325
326 // Subtract the timezone offset.
327 if (!addMinute(-(hour * 60 + minute)))
328 return false;
329 end = index;
330 return true;
331 }
332
parseMonth(const UChar * src,unsigned length,unsigned start,unsigned & end)333 bool DateComponents::parseMonth(const UChar* src, unsigned length, unsigned start, unsigned& end)
334 {
335 ASSERT(src);
336 unsigned index;
337 if (!parseYear(src, length, start, index))
338 return false;
339 if (index >= length || src[index] != '-')
340 return false;
341 ++index;
342
343 int month;
344 if (!toInt(src, length, index, 2, month) || month < 1 || month > 12)
345 return false;
346 --month;
347 if (!withinHTMLDateLimits(m_year, month))
348 return false;
349 m_month = month;
350 end = index + 2;
351 m_type = Month;
352 return true;
353 }
354
parseDate(const UChar * src,unsigned length,unsigned start,unsigned & end)355 bool DateComponents::parseDate(const UChar* src, unsigned length, unsigned start, unsigned& end)
356 {
357 ASSERT(src);
358 unsigned index;
359 if (!parseMonth(src, length, start, index))
360 return false;
361 // '-' and 2-digits are needed.
362 if (index + 2 >= length)
363 return false;
364 if (src[index] != '-')
365 return false;
366 ++index;
367
368 int day;
369 if (!toInt(src, length, index, 2, day) || day < 1 || day > maxDayOfMonth(m_year, m_month))
370 return false;
371 if (!withinHTMLDateLimits(m_year, m_month, day))
372 return false;
373 m_monthDay = day;
374 end = index + 2;
375 m_type = Date;
376 return true;
377 }
378
parseWeek(const UChar * src,unsigned length,unsigned start,unsigned & end)379 bool DateComponents::parseWeek(const UChar* src, unsigned length, unsigned start, unsigned& end)
380 {
381 ASSERT(src);
382 unsigned index;
383 if (!parseYear(src, length, start, index))
384 return false;
385
386 // 4 characters ('-' 'W' digit digit) are needed.
387 if (index + 3 >= length)
388 return false;
389 if (src[index] != '-')
390 return false;
391 ++index;
392 if (src[index] != 'W')
393 return false;
394 ++index;
395
396 int week;
397 if (!toInt(src, length, index, 2, week) || week < 1 || week > maxWeekNumberInYear())
398 return false;
399 if (m_year == maximumYear && week > maximumWeekInMaximumYear)
400 return false;
401 m_week = week;
402 end = index + 2;
403 m_type = Week;
404 return true;
405 }
406
parseTime(const UChar * src,unsigned length,unsigned start,unsigned & end)407 bool DateComponents::parseTime(const UChar* src, unsigned length, unsigned start, unsigned& end)
408 {
409 ASSERT(src);
410 int hour;
411 if (!toInt(src, length, start, 2, hour) || hour < 0 || hour > 23)
412 return false;
413 unsigned index = start + 2;
414 if (index >= length)
415 return false;
416 if (src[index] != ':')
417 return false;
418 ++index;
419
420 int minute;
421 if (!toInt(src, length, index, 2, minute) || minute < 0 || minute > 59)
422 return false;
423 index += 2;
424
425 int second = 0;
426 int millisecond = 0;
427 // Optional second part.
428 // Do not return with false because the part is optional.
429 if (index + 2 < length && src[index] == ':') {
430 if (toInt(src, length, index + 1, 2, second) && second >= 0 && second <= 59) {
431 index += 3;
432
433 // Optional fractional second part.
434 if (index < length && src[index] == '.') {
435 unsigned digitsLength = countDigits(src, length, index + 1);
436 if (digitsLength > 0) {
437 ++index;
438 bool ok;
439 if (digitsLength == 1) {
440 ok = toInt(src, length, index, 1, millisecond);
441 millisecond *= 100;
442 } else if (digitsLength == 2) {
443 ok = toInt(src, length, index, 2, millisecond);
444 millisecond *= 10;
445 } else // digitsLength >= 3
446 ok = toInt(src, length, index, 3, millisecond);
447 ASSERT(ok);
448 index += digitsLength;
449 }
450 }
451 }
452 }
453 m_hour = hour;
454 m_minute = minute;
455 m_second = second;
456 m_millisecond = millisecond;
457 end = index;
458 m_type = Time;
459 return true;
460 }
461
parseDateTimeLocal(const UChar * src,unsigned length,unsigned start,unsigned & end)462 bool DateComponents::parseDateTimeLocal(const UChar* src, unsigned length, unsigned start, unsigned& end)
463 {
464 ASSERT(src);
465 unsigned index;
466 if (!parseDate(src, length, start, index))
467 return false;
468 if (index >= length)
469 return false;
470 if (src[index] != 'T')
471 return false;
472 ++index;
473 if (!parseTime(src, length, index, end))
474 return false;
475 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond))
476 return false;
477 m_type = DateTimeLocal;
478 return true;
479 }
480
parseDateTime(const UChar * src,unsigned length,unsigned start,unsigned & end)481 bool DateComponents::parseDateTime(const UChar* src, unsigned length, unsigned start, unsigned& end)
482 {
483 ASSERT(src);
484 unsigned index;
485 if (!parseDate(src, length, start, index))
486 return false;
487 if (index >= length)
488 return false;
489 if (src[index] != 'T')
490 return false;
491 ++index;
492 if (!parseTime(src, length, index, index))
493 return false;
494 if (!parseTimeZone(src, length, index, end))
495 return false;
496 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond))
497 return false;
498 m_type = DateTime;
499 return true;
500 }
501
positiveFmod(double value,double divider)502 static inline double positiveFmod(double value, double divider)
503 {
504 double remainder = fmod(value, divider);
505 return remainder < 0 ? remainder + divider : remainder;
506 }
507
setMillisecondsSinceMidnightInternal(double msInDay)508 void DateComponents::setMillisecondsSinceMidnightInternal(double msInDay)
509 {
510 ASSERT(msInDay >= 0 && msInDay < msPerDay);
511 m_millisecond = static_cast<int>(fmod(msInDay, msPerSecond));
512 double value = floor(msInDay / msPerSecond);
513 m_second = static_cast<int>(fmod(value, secondsPerMinute));
514 value = floor(value / secondsPerMinute);
515 m_minute = static_cast<int>(fmod(value, minutesPerHour));
516 m_hour = static_cast<int>(value / minutesPerHour);
517 }
518
setMillisecondsSinceEpochForDateInternal(double ms)519 bool DateComponents::setMillisecondsSinceEpochForDateInternal(double ms)
520 {
521 m_year = msToYear(ms);
522 int yearDay = dayInYear(ms, m_year);
523 m_month = monthFromDayInYear(yearDay, isLeapYear(m_year));
524 m_monthDay = dayInMonthFromDayInYear(yearDay, isLeapYear(m_year));
525 return true;
526 }
527
setMillisecondsSinceEpochForDate(double ms)528 bool DateComponents::setMillisecondsSinceEpochForDate(double ms)
529 {
530 m_type = Invalid;
531 if (!isfinite(ms))
532 return false;
533 if (!setMillisecondsSinceEpochForDateInternal(round(ms)))
534 return false;
535 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay))
536 return false;
537 m_type = Date;
538 return true;
539 }
540
setMillisecondsSinceEpochForDateTime(double ms)541 bool DateComponents::setMillisecondsSinceEpochForDateTime(double ms)
542 {
543 m_type = Invalid;
544 if (!isfinite(ms))
545 return false;
546 ms = round(ms);
547 setMillisecondsSinceMidnightInternal(positiveFmod(ms, msPerDay));
548 if (!setMillisecondsSinceEpochForDateInternal(ms))
549 return false;
550 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond))
551 return false;
552 m_type = DateTime;
553 return true;
554 }
555
setMillisecondsSinceEpochForDateTimeLocal(double ms)556 bool DateComponents::setMillisecondsSinceEpochForDateTimeLocal(double ms)
557 {
558 // Internal representation of DateTimeLocal is the same as DateTime except m_type.
559 if (!setMillisecondsSinceEpochForDateTime(ms))
560 return false;
561 m_type = DateTimeLocal;
562 return true;
563 }
564
setMillisecondsSinceEpochForMonth(double ms)565 bool DateComponents::setMillisecondsSinceEpochForMonth(double ms)
566 {
567 m_type = Invalid;
568 if (!isfinite(ms))
569 return false;
570 if (!setMillisecondsSinceEpochForDateInternal(round(ms)))
571 return false;
572 if (!withinHTMLDateLimits(m_year, m_month))
573 return false;
574 m_type = Month;
575 return true;
576 }
577
setMillisecondsSinceMidnight(double ms)578 bool DateComponents::setMillisecondsSinceMidnight(double ms)
579 {
580 m_type = Invalid;
581 if (!isfinite(ms))
582 return false;
583 setMillisecondsSinceMidnightInternal(positiveFmod(round(ms), msPerDay));
584 m_type = Time;
585 return true;
586 }
587
setMonthsSinceEpoch(double months)588 bool DateComponents::setMonthsSinceEpoch(double months)
589 {
590 if (!isfinite(months))
591 return false;
592 months = round(months);
593 double doubleMonth = positiveFmod(months, 12);
594 double doubleYear = 1970 + (months - doubleMonth) / 12;
595 if (doubleYear < minimumYear || maximumYear < doubleYear)
596 return false;
597 int year = static_cast<int>(doubleYear);
598 int month = static_cast<int>(doubleMonth);
599 if (!withinHTMLDateLimits(year, month))
600 return false;
601 m_year = year;
602 m_month = month;
603 m_type = Month;
604 return true;
605 }
606
607 // Offset from January 1st to Monday of the ISO 8601's first week.
608 // ex. If January 1st is Friday, such Monday is 3 days later. Returns 3.
offsetTo1stWeekStart(int year)609 static int offsetTo1stWeekStart(int year)
610 {
611 int offsetTo1stWeekStart = 1 - dayOfWeek(year, 0, 1);
612 if (offsetTo1stWeekStart <= -4)
613 offsetTo1stWeekStart += 7;
614 return offsetTo1stWeekStart;
615 }
616
setMillisecondsSinceEpochForWeek(double ms)617 bool DateComponents::setMillisecondsSinceEpochForWeek(double ms)
618 {
619 m_type = Invalid;
620 if (!isfinite(ms))
621 return false;
622 ms = round(ms);
623
624 m_year = msToYear(ms);
625 if (m_year < minimumYear || m_year > maximumYear)
626 return false;
627
628 int yearDay = dayInYear(ms, m_year);
629 int offset = offsetTo1stWeekStart(m_year);
630 if (yearDay < offset) {
631 // The day belongs to the last week of the previous year.
632 m_year--;
633 if (m_year <= minimumYear)
634 return false;
635 m_week = maxWeekNumberInYear();
636 } else {
637 m_week = ((yearDay - offset) / 7) + 1;
638 if (m_week > maxWeekNumberInYear()) {
639 m_year++;
640 m_week = 1;
641 }
642 if (m_year > maximumYear || (m_year == maximumYear && m_week > maximumWeekInMaximumYear))
643 return false;
644 }
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 WebCore
729