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