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