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 "core/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 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. 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. 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 95 int DateComponents::maxWeekNumberInYear() const 96 { 97 int day = dayOfWeek(m_year, 0, 1); // January 1. 98 return day == Thursday || (day == Wednesday && isLeapYear(m_year)) ? maximumWeekNumber : maximumWeekNumber - 1; 99 } 100 101 static unsigned countDigits(const String& src, unsigned start) 102 { 103 unsigned index = start; 104 for (; index < src.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(). 112 static bool toInt(const String& src, unsigned parseStart, unsigned parseLength, int& out) 113 { 114 if (parseStart + parseLength > src.length() || parseLength <= 0) 115 return false; 116 int value = 0; 117 unsigned current = parseStart; 118 unsigned end = current + parseLength; 119 120 // We don't need to handle negative numbers for ISO 8601. 121 for (; current < end; ++current) { 122 if (!isASCIIDigit(src[current])) 123 return false; 124 int digit = src[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 133 bool DateComponents::parseYear(const String& src, unsigned start, unsigned& end) 134 { 135 unsigned digitsLength = countDigits(src, 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, 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 149 static bool withinHTMLDateLimits(int year, int month) 150 { 151 if (year < DateComponents::minimumYear()) 152 return false; 153 if (year < DateComponents::maximumYear()) 154 return true; 155 return month <= maximumMonthInMaximumYear; 156 } 157 158 static bool withinHTMLDateLimits(int year, int month, int monthDay) 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 return monthDay <= maximumDayInMaximumMonth; 167 } 168 169 static bool withinHTMLDateLimits(int year, int month, int monthDay, int hour, int minute, int second, int millisecond) 170 { 171 if (year < DateComponents::minimumYear()) 172 return false; 173 if (year < DateComponents::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 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 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 (minute < 0) { 251 carry = (59 - 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. 288 bool DateComponents::parseTimeZone(const String& src, unsigned start, unsigned& end) 289 { 290 if (start >= src.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, index, 2, hour) || hour < 0 || hour > 23) 310 return false; 311 index += 2; 312 313 if (index >= src.length() || src[index] != ':') 314 return false; 315 ++index; 316 317 if (!toInt(src, 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 333 bool DateComponents::parseMonth(const String& src, unsigned start, unsigned& end) 334 { 335 unsigned index; 336 if (!parseYear(src, start, index)) 337 return false; 338 if (index >= src.length() || src[index] != '-') 339 return false; 340 ++index; 341 342 int month; 343 if (!toInt(src, index, 2, month) || month < 1 || month > 12) 344 return false; 345 --month; 346 if (!withinHTMLDateLimits(m_year, month)) 347 return false; 348 m_month = month; 349 end = index + 2; 350 m_type = Month; 351 return true; 352 } 353 354 bool DateComponents::parseDate(const String& src, unsigned start, unsigned& end) 355 { 356 unsigned index; 357 if (!parseMonth(src, start, index)) 358 return false; 359 // '-' and 2-digits are needed. 360 if (index + 2 >= src.length()) 361 return false; 362 if (src[index] != '-') 363 return false; 364 ++index; 365 366 int day; 367 if (!toInt(src, 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 String& src, unsigned start, unsigned& end) 378 { 379 unsigned index; 380 if (!parseYear(src, start, index)) 381 return false; 382 383 // 4 characters ('-' 'W' digit digit) are needed. 384 if (index + 3 >= src.length()) 385 return false; 386 if (src[index] != '-') 387 return false; 388 ++index; 389 if (src[index] != 'W') 390 return false; 391 ++index; 392 393 int week; 394 if (!toInt(src, index, 2, week) || week < minimumWeekNumber || week > maxWeekNumberInYear()) 395 return false; 396 if (m_year == maximumYear() && week > maximumWeekInMaximumYear) 397 return false; 398 m_week = week; 399 end = index + 2; 400 m_type = Week; 401 return true; 402 } 403 404 bool DateComponents::parseTime(const String& src, unsigned start, unsigned& end) 405 { 406 int hour; 407 if (!toInt(src, start, 2, hour) || hour < 0 || hour > 23) 408 return false; 409 unsigned index = start + 2; 410 if (index >= src.length()) 411 return false; 412 if (src[index] != ':') 413 return false; 414 ++index; 415 416 int minute; 417 if (!toInt(src, index, 2, minute) || minute < 0 || minute > 59) 418 return false; 419 index += 2; 420 421 int second = 0; 422 int millisecond = 0; 423 // Optional second part. 424 // Do not return with false because the part is optional. 425 if (index + 2 < src.length() && src[index] == ':') { 426 if (toInt(src, index + 1, 2, second) && second >= 0 && second <= 59) { 427 index += 3; 428 429 // Optional fractional second part. 430 if (index < src.length() && src[index] == '.') { 431 unsigned digitsLength = countDigits(src, index + 1); 432 if (digitsLength > 0) { 433 ++index; 434 bool ok; 435 if (digitsLength == 1) { 436 ok = toInt(src, index, 1, millisecond); 437 millisecond *= 100; 438 } else if (digitsLength == 2) { 439 ok = toInt(src, index, 2, millisecond); 440 millisecond *= 10; 441 } else { // digitsLength >= 3 442 ok = toInt(src, index, 3, millisecond); 443 } 444 ASSERT_UNUSED(ok, ok); 445 index += digitsLength; 446 } 447 } 448 } 449 } 450 m_hour = hour; 451 m_minute = minute; 452 m_second = second; 453 m_millisecond = millisecond; 454 end = index; 455 m_type = Time; 456 return true; 457 } 458 459 bool DateComponents::parseDateTimeLocal(const String& src, unsigned start, unsigned& end) 460 { 461 unsigned index; 462 if (!parseDate(src, start, index)) 463 return false; 464 if (index >= src.length()) 465 return false; 466 if (src[index] != 'T') 467 return false; 468 ++index; 469 if (!parseTime(src, index, end)) 470 return false; 471 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond)) 472 return false; 473 m_type = DateTimeLocal; 474 return true; 475 } 476 477 bool DateComponents::parseDateTime(const String& src, unsigned start, unsigned& end) 478 { 479 unsigned index; 480 if (!parseDate(src, start, index)) 481 return false; 482 if (index >= src.length()) 483 return false; 484 if (src[index] != 'T') 485 return false; 486 ++index; 487 if (!parseTime(src, index, index)) 488 return false; 489 if (!parseTimeZone(src, index, end)) 490 return false; 491 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond)) 492 return false; 493 m_type = DateTime; 494 return true; 495 } 496 497 static inline double positiveFmod(double value, double divider) 498 { 499 double remainder = fmod(value, divider); 500 return remainder < 0 ? remainder + divider : remainder; 501 } 502 503 void DateComponents::setMillisecondsSinceMidnightInternal(double msInDay) 504 { 505 ASSERT(msInDay >= 0 && msInDay < msPerDay); 506 m_millisecond = static_cast<int>(fmod(msInDay, msPerSecond)); 507 double value = floor(msInDay / msPerSecond); 508 m_second = static_cast<int>(fmod(value, secondsPerMinute)); 509 value = floor(value / secondsPerMinute); 510 m_minute = static_cast<int>(fmod(value, minutesPerHour)); 511 m_hour = static_cast<int>(value / minutesPerHour); 512 } 513 514 bool DateComponents::setMillisecondsSinceEpochForDateInternal(double ms) 515 { 516 m_year = msToYear(ms); 517 int yearDay = dayInYear(ms, m_year); 518 m_month = monthFromDayInYear(yearDay, isLeapYear(m_year)); 519 m_monthDay = dayInMonthFromDayInYear(yearDay, isLeapYear(m_year)); 520 return true; 521 } 522 523 bool DateComponents::setMillisecondsSinceEpochForDate(double ms) 524 { 525 m_type = Invalid; 526 if (!std::isfinite(ms)) 527 return false; 528 if (!setMillisecondsSinceEpochForDateInternal(round(ms))) 529 return false; 530 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay)) 531 return false; 532 m_type = Date; 533 return true; 534 } 535 536 bool DateComponents::setMillisecondsSinceEpochForDateTime(double ms) 537 { 538 m_type = Invalid; 539 if (!std::isfinite(ms)) 540 return false; 541 ms = round(ms); 542 setMillisecondsSinceMidnightInternal(positiveFmod(ms, msPerDay)); 543 if (!setMillisecondsSinceEpochForDateInternal(ms)) 544 return false; 545 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond)) 546 return false; 547 m_type = DateTime; 548 return true; 549 } 550 551 bool DateComponents::setMillisecondsSinceEpochForDateTimeLocal(double ms) 552 { 553 // Internal representation of DateTimeLocal is the same as DateTime except m_type. 554 if (!setMillisecondsSinceEpochForDateTime(ms)) 555 return false; 556 m_type = DateTimeLocal; 557 return true; 558 } 559 560 bool DateComponents::setMillisecondsSinceEpochForMonth(double ms) 561 { 562 m_type = Invalid; 563 if (!std::isfinite(ms)) 564 return false; 565 if (!setMillisecondsSinceEpochForDateInternal(round(ms))) 566 return false; 567 if (!withinHTMLDateLimits(m_year, m_month)) 568 return false; 569 m_type = Month; 570 return true; 571 } 572 573 bool DateComponents::setMillisecondsSinceMidnight(double ms) 574 { 575 m_type = Invalid; 576 if (!std::isfinite(ms)) 577 return false; 578 setMillisecondsSinceMidnightInternal(positiveFmod(round(ms), msPerDay)); 579 m_type = Time; 580 return true; 581 } 582 583 bool DateComponents::setMonthsSinceEpoch(double months) 584 { 585 if (!std::isfinite(months)) 586 return false; 587 months = round(months); 588 double doubleMonth = positiveFmod(months, 12); 589 double doubleYear = 1970 + (months - doubleMonth) / 12; 590 if (doubleYear < minimumYear() || maximumYear() < doubleYear) 591 return false; 592 int year = static_cast<int>(doubleYear); 593 int month = static_cast<int>(doubleMonth); 594 if (!withinHTMLDateLimits(year, month)) 595 return false; 596 m_year = year; 597 m_month = month; 598 m_type = Month; 599 return true; 600 } 601 602 // Offset from January 1st to Monday of the ISO 8601's first week. 603 // ex. If January 1st is Friday, such Monday is 3 days later. Returns 3. 604 static int offsetTo1stWeekStart(int year) 605 { 606 int offsetTo1stWeekStart = 1 - dayOfWeek(year, 0, 1); 607 if (offsetTo1stWeekStart <= -4) 608 offsetTo1stWeekStart += 7; 609 return offsetTo1stWeekStart; 610 } 611 612 bool DateComponents::setMillisecondsSinceEpochForWeek(double ms) 613 { 614 m_type = Invalid; 615 if (!std::isfinite(ms)) 616 return false; 617 ms = round(ms); 618 619 m_year = msToYear(ms); 620 if (m_year < minimumYear() || m_year > maximumYear()) 621 return false; 622 623 int yearDay = dayInYear(ms, m_year); 624 int offset = offsetTo1stWeekStart(m_year); 625 if (yearDay < offset) { 626 // The day belongs to the last week of the previous year. 627 m_year--; 628 if (m_year <= minimumYear()) 629 return false; 630 m_week = maxWeekNumberInYear(); 631 } else { 632 m_week = ((yearDay - offset) / 7) + 1; 633 if (m_week > maxWeekNumberInYear()) { 634 m_year++; 635 m_week = 1; 636 } 637 if (m_year > maximumYear() || (m_year == maximumYear() && m_week > maximumWeekInMaximumYear)) 638 return false; 639 } 640 m_type = Week; 641 return true; 642 } 643 644 double DateComponents::millisecondsSinceEpochForTime() const 645 { 646 ASSERT(m_type == Time || m_type == DateTime || m_type == DateTimeLocal); 647 return ((m_hour * minutesPerHour + m_minute) * secondsPerMinute + m_second) * msPerSecond + m_millisecond; 648 } 649 650 double DateComponents::millisecondsSinceEpoch() const 651 { 652 switch (m_type) { 653 case Date: 654 return dateToDaysFrom1970(m_year, m_month, m_monthDay) * msPerDay; 655 case DateTime: 656 case DateTimeLocal: 657 return dateToDaysFrom1970(m_year, m_month, m_monthDay) * msPerDay + millisecondsSinceEpochForTime(); 658 case Month: 659 return dateToDaysFrom1970(m_year, m_month, 1) * msPerDay; 660 case Time: 661 return millisecondsSinceEpochForTime(); 662 case Week: 663 return (dateToDaysFrom1970(m_year, 0, 1) + offsetTo1stWeekStart(m_year) + (m_week - 1) * 7) * msPerDay; 664 case Invalid: 665 break; 666 } 667 ASSERT_NOT_REACHED(); 668 return invalidMilliseconds(); 669 } 670 671 double DateComponents::monthsSinceEpoch() const 672 { 673 ASSERT(m_type == Month); 674 return (m_year - 1970) * 12 + m_month; 675 } 676 677 String DateComponents::toStringForTime(SecondFormat format) const 678 { 679 ASSERT(m_type == DateTime || m_type == DateTimeLocal || m_type == Time); 680 SecondFormat effectiveFormat = format; 681 if (m_millisecond) 682 effectiveFormat = Millisecond; 683 else if (format == None && m_second) 684 effectiveFormat = Second; 685 686 switch (effectiveFormat) { 687 default: 688 ASSERT_NOT_REACHED(); 689 // Fallback to None. 690 case None: 691 return String::format("%02d:%02d", m_hour, m_minute); 692 case Second: 693 return String::format("%02d:%02d:%02d", m_hour, m_minute, m_second); 694 case Millisecond: 695 return String::format("%02d:%02d:%02d.%03d", m_hour, m_minute, m_second, m_millisecond); 696 } 697 } 698 699 String DateComponents::toString(SecondFormat format) const 700 { 701 switch (m_type) { 702 case Date: 703 return String::format("%04d-%02d-%02d", m_year, m_month + 1, m_monthDay); 704 case DateTime: 705 return String::format("%04d-%02d-%02dT", m_year, m_month + 1, m_monthDay) 706 + toStringForTime(format) + String("Z"); 707 case DateTimeLocal: 708 return String::format("%04d-%02d-%02dT", m_year, m_month + 1, m_monthDay) 709 + toStringForTime(format); 710 case Month: 711 return String::format("%04d-%02d", m_year, m_month + 1); 712 case Time: 713 return toStringForTime(format); 714 case Week: 715 return String::format("%04d-W%02d", m_year, m_week); 716 case Invalid: 717 break; 718 } 719 ASSERT_NOT_REACHED(); 720 return String("(Invalid DateComponents)"); 721 } 722 723 } // namespace WebCore 724