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/WTFString.h> 39 40namespace WebCore { 41 42// HTML5 specification defines minimum week of year is one. 43const int DateComponents::minimumWeekNumber = 1; 44 45// HTML5 specification defines maximum week of year is 53. 46const int DateComponents::maximumWeekNumber = 53; 47 48static const int maximumMonthInMaximumYear = 8; // This is September, since months are 0 based. 49static const int maximumDayInMaximumMonth = 13; 50static const int maximumWeekInMaximumYear = 37; // The week of 275760-09-13 51 52static const int daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 53 54static 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. 66static 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. 74static 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 93int 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 99static 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(). 110static 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 131bool 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 147static 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 156static 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 167static 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 183bool 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 236bool 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. 286bool 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 331bool 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 353bool 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 377bool 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 405bool 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 460bool 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 479bool 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 500static inline double positiveFmod(double value, double divider) 501{ 502 double remainder = fmod(value, divider); 503 return remainder < 0 ? remainder + divider : remainder; 504} 505 506void 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 517bool 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 526bool 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 539bool 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 554bool 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 563bool 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 576bool 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 586bool 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. 607static 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 615bool 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 647double 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 653double 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 674double DateComponents::monthsSinceEpoch() const 675{ 676 ASSERT(m_type == Month); 677 return (m_year - 1970) * 12 + m_month; 678} 679 680String 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 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 704String 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