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::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 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(). 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 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 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 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 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 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 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. 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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. 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 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 633 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 639 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 660 double DateComponents::monthsSinceEpoch() const 661 { 662 ASSERT(m_type == Month); 663 return (m_year - 1970) * 12 + m_month; 664 } 665 666 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 688 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