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. 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. 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 84 int DateComponents::weekDay() const 85 { 86 return dayOfWeek(m_year, m_month, m_monthDay); 87 } 88 89 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 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(). 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 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 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 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 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 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 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. 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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. 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 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 638 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 644 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 665 double DateComponents::monthsSinceEpoch() const 666 { 667 ASSERT(m_type == Month); 668 return (m_year - 1970) * 12 + m_month; 669 } 670 671 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 693 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