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 namespace blink { 41 42 // HTML5 specification defines minimum week of year is one. 43 const int DateComponents::minimumWeekNumber = 1; 44 45 // HTML5 specification defines maximum week of year is 53. 46 const int DateComponents::maximumWeekNumber = 53; 47 48 static const int maximumMonthInMaximumYear = 8; // This is September, since months are 0 based. 49 static const int maximumDayInMaximumMonth = 13; 50 static const int maximumWeekInMaximumYear = 37; // The week of 275760-09-13 51 52 static const int daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 53 54 // 'month' is 0-based. 55 static int maxDayOfMonth(int year, int month) 56 { 57 if (month != 1) // February? 58 return daysInMonth[month]; 59 return isLeapYear(year) ? 29 : 28; 60 } 61 62 // 'month' is 0-based. 63 static int dayOfWeek(int year, int month, int day) 64 { 65 int shiftedMonth = month + 2; 66 // 2:January, 3:Feburuary, 4:March, ... 67 68 // Zeller's congruence 69 if (shiftedMonth <= 3) { 70 shiftedMonth += 12; 71 year--; 72 } 73 // 4:March, ..., 14:January, 15:February 74 75 int highYear = year / 100; 76 int lowYear = year % 100; 77 // We add 6 to make the result Sunday-origin. 78 int result = (day + 13 * shiftedMonth / 5 + lowYear + lowYear / 4 + highYear / 4 + 5 * highYear + 6) % 7; 79 return result; 80 } 81 82 int DateComponents::weekDay() const 83 { 84 return dayOfWeek(m_year, m_month, m_monthDay); 85 } 86 87 int DateComponents::maxWeekNumberInYear() const 88 { 89 int day = dayOfWeek(m_year, 0, 1); // January 1. 90 return day == Thursday || (day == Wednesday && isLeapYear(m_year)) ? maximumWeekNumber : maximumWeekNumber - 1; 91 } 92 93 static unsigned countDigits(const String& src, unsigned start) 94 { 95 unsigned index = start; 96 for (; index < src.length(); ++index) { 97 if (!isASCIIDigit(src[index])) 98 break; 99 } 100 return index - start; 101 } 102 103 // Very strict integer parser. Do not allow leading or trailing whitespace unlike charactersToIntStrict(). 104 static bool toInt(const String& src, unsigned parseStart, unsigned parseLength, int& out) 105 { 106 if (parseStart + parseLength > src.length() || !parseLength) 107 return false; 108 int value = 0; 109 unsigned current = parseStart; 110 unsigned end = current + parseLength; 111 112 // We don't need to handle negative numbers for ISO 8601. 113 for (; current < end; ++current) { 114 if (!isASCIIDigit(src[current])) 115 return false; 116 int digit = src[current] - '0'; 117 if (value > (INT_MAX - digit) / 10) // Check for overflow. 118 return false; 119 value = value * 10 + digit; 120 } 121 out = value; 122 return true; 123 } 124 125 bool DateComponents::parseYear(const String& src, unsigned start, unsigned& end) 126 { 127 unsigned digitsLength = countDigits(src, start); 128 // Needs at least 4 digits according to the standard. 129 if (digitsLength < 4) 130 return false; 131 int year; 132 if (!toInt(src, start, digitsLength, year)) 133 return false; 134 if (year < minimumYear() || year > maximumYear()) 135 return false; 136 m_year = year; 137 end = start + digitsLength; 138 return true; 139 } 140 141 static bool withinHTMLDateLimits(int year, int month) 142 { 143 if (year < DateComponents::minimumYear()) 144 return false; 145 if (year < DateComponents::maximumYear()) 146 return true; 147 return month <= maximumMonthInMaximumYear; 148 } 149 150 static bool withinHTMLDateLimits(int year, int month, int monthDay) 151 { 152 if (year < DateComponents::minimumYear()) 153 return false; 154 if (year < DateComponents::maximumYear()) 155 return true; 156 if (month < maximumMonthInMaximumYear) 157 return true; 158 return monthDay <= maximumDayInMaximumMonth; 159 } 160 161 static bool withinHTMLDateLimits(int year, int month, int monthDay, int hour, int minute, int second, int millisecond) 162 { 163 if (year < DateComponents::minimumYear()) 164 return false; 165 if (year < DateComponents::maximumYear()) 166 return true; 167 if (month < maximumMonthInMaximumYear) 168 return true; 169 if (monthDay < maximumDayInMaximumMonth) 170 return true; 171 if (monthDay > maximumDayInMaximumMonth) 172 return false; 173 // (year, month, monthDay) = (maximumYear, maximumMonthInMaximumYear, maximumDayInMaximumMonth) 174 return !hour && !minute && !second && !millisecond; 175 } 176 177 bool DateComponents::addDay(int dayDiff) 178 { 179 ASSERT(m_monthDay); 180 181 int day = m_monthDay + dayDiff; 182 if (day > maxDayOfMonth(m_year, m_month)) { 183 day = m_monthDay; 184 int year = m_year; 185 int month = m_month; 186 int maxDay = maxDayOfMonth(year, month); 187 for (; dayDiff > 0; --dayDiff) { 188 ++day; 189 if (day > maxDay) { 190 day = 1; 191 ++month; 192 if (month >= 12) { // month is 0-origin. 193 month = 0; 194 ++year; 195 } 196 maxDay = maxDayOfMonth(year, month); 197 } 198 } 199 if (!withinHTMLDateLimits(year, month, day)) 200 return false; 201 m_year = year; 202 m_month = month; 203 } else if (day < 1) { 204 int month = m_month; 205 int year = m_year; 206 day = m_monthDay; 207 for (; dayDiff < 0; ++dayDiff) { 208 --day; 209 if (day < 1) { 210 --month; 211 if (month < 0) { 212 month = 11; 213 --year; 214 } 215 day = maxDayOfMonth(year, month); 216 } 217 } 218 if (!withinHTMLDateLimits(year, month, day)) 219 return false; 220 m_year = year; 221 m_month = month; 222 } else { 223 if (!withinHTMLDateLimits(m_year, m_month, day)) 224 return false; 225 } 226 m_monthDay = day; 227 return true; 228 } 229 230 bool DateComponents::addMinute(int minute) 231 { 232 // This function is used to adjust timezone offset. So m_year, m_month, 233 // m_monthDay have values between the lower and higher limits. 234 ASSERT(withinHTMLDateLimits(m_year, m_month, m_monthDay)); 235 236 int carry; 237 // minute can be negative or greater than 59. 238 minute += m_minute; 239 if (minute > 59) { 240 carry = minute / 60; 241 minute = minute % 60; 242 } else if (minute < 0) { 243 carry = (59 - minute) / 60; 244 minute += carry * 60; 245 carry = -carry; 246 ASSERT(minute >= 0 && minute <= 59); 247 } else { 248 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, minute, m_second, m_millisecond)) 249 return false; 250 m_minute = minute; 251 return true; 252 } 253 254 int hour = m_hour + carry; 255 if (hour > 23) { 256 carry = hour / 24; 257 hour = hour % 24; 258 } else if (hour < 0) { 259 carry = (23 - hour) / 24; 260 hour += carry * 24; 261 carry = -carry; 262 ASSERT(hour >= 0 && hour <= 23); 263 } else { 264 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, hour, minute, m_second, m_millisecond)) 265 return false; 266 m_minute = minute; 267 m_hour = hour; 268 return true; 269 } 270 if (!addDay(carry)) 271 return false; 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 279 // Parses a timezone part, and adjust year, month, monthDay, hour, minute, second, millisecond. 280 bool DateComponents::parseTimeZone(const String& src, unsigned start, unsigned& end) 281 { 282 if (start >= src.length()) 283 return false; 284 unsigned index = start; 285 if (src[index] == 'Z') { 286 end = index + 1; 287 return true; 288 } 289 290 bool minus; 291 if (src[index] == '+') 292 minus = false; 293 else if (src[index] == '-') 294 minus = true; 295 else 296 return false; 297 ++index; 298 299 int hour; 300 int minute; 301 if (!toInt(src, index, 2, hour) || hour < 0 || hour > 23) 302 return false; 303 index += 2; 304 305 if (index >= src.length() || src[index] != ':') 306 return false; 307 ++index; 308 309 if (!toInt(src, index, 2, minute) || minute < 0 || minute > 59) 310 return false; 311 index += 2; 312 313 if (minus) { 314 hour = -hour; 315 minute = -minute; 316 } 317 318 // Subtract the timezone offset. 319 if (!addMinute(-(hour * 60 + minute))) 320 return false; 321 end = index; 322 return true; 323 } 324 325 bool DateComponents::parseMonth(const String& src, unsigned start, unsigned& end) 326 { 327 unsigned index; 328 if (!parseYear(src, start, index)) 329 return false; 330 if (index >= src.length() || src[index] != '-') 331 return false; 332 ++index; 333 334 int month; 335 if (!toInt(src, index, 2, month) || month < 1 || month > 12) 336 return false; 337 --month; 338 if (!withinHTMLDateLimits(m_year, month)) 339 return false; 340 m_month = month; 341 end = index + 2; 342 m_type = Month; 343 return true; 344 } 345 346 bool DateComponents::parseDate(const String& src, unsigned start, unsigned& end) 347 { 348 unsigned index; 349 if (!parseMonth(src, start, index)) 350 return false; 351 // '-' and 2-digits are needed. 352 if (index + 2 >= src.length()) 353 return false; 354 if (src[index] != '-') 355 return false; 356 ++index; 357 358 int day; 359 if (!toInt(src, index, 2, day) || day < 1 || day > maxDayOfMonth(m_year, m_month)) 360 return false; 361 if (!withinHTMLDateLimits(m_year, m_month, day)) 362 return false; 363 m_monthDay = day; 364 end = index + 2; 365 m_type = Date; 366 return true; 367 } 368 369 bool DateComponents::parseWeek(const String& src, unsigned start, unsigned& end) 370 { 371 unsigned index; 372 if (!parseYear(src, start, index)) 373 return false; 374 375 // 4 characters ('-' 'W' digit digit) are needed. 376 if (index + 3 >= src.length()) 377 return false; 378 if (src[index] != '-') 379 return false; 380 ++index; 381 if (src[index] != 'W') 382 return false; 383 ++index; 384 385 int week; 386 if (!toInt(src, index, 2, week) || week < minimumWeekNumber || week > maxWeekNumberInYear()) 387 return false; 388 if (m_year == maximumYear() && week > maximumWeekInMaximumYear) 389 return false; 390 m_week = week; 391 end = index + 2; 392 m_type = Week; 393 return true; 394 } 395 396 bool DateComponents::parseTime(const String& src, unsigned start, unsigned& end) 397 { 398 int hour; 399 if (!toInt(src, start, 2, hour) || hour < 0 || hour > 23) 400 return false; 401 unsigned index = start + 2; 402 if (index >= src.length()) 403 return false; 404 if (src[index] != ':') 405 return false; 406 ++index; 407 408 int minute; 409 if (!toInt(src, index, 2, minute) || minute < 0 || minute > 59) 410 return false; 411 index += 2; 412 413 int second = 0; 414 int millisecond = 0; 415 // Optional second part. 416 // Do not return with false because the part is optional. 417 if (index + 2 < src.length() && src[index] == ':') { 418 if (toInt(src, index + 1, 2, second) && second >= 0 && second <= 59) { 419 index += 3; 420 421 // Optional fractional second part. 422 if (index < src.length() && src[index] == '.') { 423 unsigned digitsLength = countDigits(src, index + 1); 424 if (digitsLength > 0) { 425 ++index; 426 bool ok; 427 if (digitsLength == 1) { 428 ok = toInt(src, index, 1, millisecond); 429 millisecond *= 100; 430 } else if (digitsLength == 2) { 431 ok = toInt(src, index, 2, millisecond); 432 millisecond *= 10; 433 } else { // digitsLength >= 3 434 ok = toInt(src, index, 3, millisecond); 435 } 436 ASSERT_UNUSED(ok, ok); 437 index += digitsLength; 438 } 439 } 440 } 441 } 442 m_hour = hour; 443 m_minute = minute; 444 m_second = second; 445 m_millisecond = millisecond; 446 end = index; 447 m_type = Time; 448 return true; 449 } 450 451 bool DateComponents::parseDateTimeLocal(const String& src, unsigned start, unsigned& end) 452 { 453 unsigned index; 454 if (!parseDate(src, start, index)) 455 return false; 456 if (index >= src.length()) 457 return false; 458 if (src[index] != 'T') 459 return false; 460 ++index; 461 if (!parseTime(src, index, end)) 462 return false; 463 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond)) 464 return false; 465 m_type = DateTimeLocal; 466 return true; 467 } 468 469 bool DateComponents::parseDateTime(const String& src, unsigned start, unsigned& end) 470 { 471 unsigned index; 472 if (!parseDate(src, start, index)) 473 return false; 474 if (index >= src.length()) 475 return false; 476 if (src[index] != 'T') 477 return false; 478 ++index; 479 if (!parseTime(src, index, index)) 480 return false; 481 if (!parseTimeZone(src, index, end)) 482 return false; 483 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond)) 484 return false; 485 m_type = DateTime; 486 return true; 487 } 488 489 static inline double positiveFmod(double value, double divider) 490 { 491 double remainder = fmod(value, divider); 492 return remainder < 0 ? remainder + divider : remainder; 493 } 494 495 void DateComponents::setMillisecondsSinceMidnightInternal(double msInDay) 496 { 497 ASSERT(msInDay >= 0 && msInDay < msPerDay); 498 m_millisecond = static_cast<int>(fmod(msInDay, msPerSecond)); 499 double value = std::floor(msInDay / msPerSecond); 500 m_second = static_cast<int>(fmod(value, secondsPerMinute)); 501 value = std::floor(value / secondsPerMinute); 502 m_minute = static_cast<int>(fmod(value, minutesPerHour)); 503 m_hour = static_cast<int>(value / minutesPerHour); 504 } 505 506 bool DateComponents::setMillisecondsSinceEpochForDateInternal(double ms) 507 { 508 m_year = msToYear(ms); 509 int yearDay = dayInYear(ms, m_year); 510 m_month = monthFromDayInYear(yearDay, isLeapYear(m_year)); 511 m_monthDay = dayInMonthFromDayInYear(yearDay, isLeapYear(m_year)); 512 return true; 513 } 514 515 bool DateComponents::setMillisecondsSinceEpochForDate(double ms) 516 { 517 m_type = Invalid; 518 if (!std::isfinite(ms)) 519 return false; 520 if (!setMillisecondsSinceEpochForDateInternal(round(ms))) 521 return false; 522 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay)) 523 return false; 524 m_type = Date; 525 return true; 526 } 527 528 bool DateComponents::setMillisecondsSinceEpochForDateTime(double ms) 529 { 530 m_type = Invalid; 531 if (!std::isfinite(ms)) 532 return false; 533 ms = round(ms); 534 setMillisecondsSinceMidnightInternal(positiveFmod(ms, msPerDay)); 535 if (!setMillisecondsSinceEpochForDateInternal(ms)) 536 return false; 537 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond)) 538 return false; 539 m_type = DateTime; 540 return true; 541 } 542 543 bool DateComponents::setMillisecondsSinceEpochForDateTimeLocal(double ms) 544 { 545 // Internal representation of DateTimeLocal is the same as DateTime except m_type. 546 if (!setMillisecondsSinceEpochForDateTime(ms)) 547 return false; 548 m_type = DateTimeLocal; 549 return true; 550 } 551 552 bool DateComponents::setMillisecondsSinceEpochForMonth(double ms) 553 { 554 m_type = Invalid; 555 if (!std::isfinite(ms)) 556 return false; 557 if (!setMillisecondsSinceEpochForDateInternal(round(ms))) 558 return false; 559 if (!withinHTMLDateLimits(m_year, m_month)) 560 return false; 561 m_type = Month; 562 return true; 563 } 564 565 bool DateComponents::setMillisecondsSinceMidnight(double ms) 566 { 567 m_type = Invalid; 568 if (!std::isfinite(ms)) 569 return false; 570 setMillisecondsSinceMidnightInternal(positiveFmod(round(ms), msPerDay)); 571 m_type = Time; 572 return true; 573 } 574 575 bool DateComponents::setMonthsSinceEpoch(double months) 576 { 577 if (!std::isfinite(months)) 578 return false; 579 months = round(months); 580 double doubleMonth = positiveFmod(months, 12); 581 double doubleYear = 1970 + (months - doubleMonth) / 12; 582 if (doubleYear < minimumYear() || maximumYear() < doubleYear) 583 return false; 584 int year = static_cast<int>(doubleYear); 585 int month = static_cast<int>(doubleMonth); 586 if (!withinHTMLDateLimits(year, month)) 587 return false; 588 m_year = year; 589 m_month = month; 590 m_type = Month; 591 return true; 592 } 593 594 // Offset from January 1st to Monday of the ISO 8601's first week. 595 // ex. If January 1st is Friday, such Monday is 3 days later. Returns 3. 596 static int offsetTo1stWeekStart(int year) 597 { 598 int offsetTo1stWeekStart = 1 - dayOfWeek(year, 0, 1); 599 if (offsetTo1stWeekStart <= -4) 600 offsetTo1stWeekStart += 7; 601 return offsetTo1stWeekStart; 602 } 603 604 bool DateComponents::setMillisecondsSinceEpochForWeek(double ms) 605 { 606 m_type = Invalid; 607 if (!std::isfinite(ms)) 608 return false; 609 ms = round(ms); 610 611 m_year = msToYear(ms); 612 if (m_year < minimumYear() || m_year > maximumYear()) 613 return false; 614 615 int yearDay = dayInYear(ms, m_year); 616 int offset = offsetTo1stWeekStart(m_year); 617 if (yearDay < offset) { 618 // The day belongs to the last week of the previous year. 619 m_year--; 620 if (m_year <= minimumYear()) 621 return false; 622 m_week = maxWeekNumberInYear(); 623 } else { 624 m_week = ((yearDay - offset) / 7) + 1; 625 if (m_week > maxWeekNumberInYear()) { 626 m_year++; 627 m_week = 1; 628 } 629 if (m_year > maximumYear() || (m_year == maximumYear() && m_week > maximumWeekInMaximumYear)) 630 return false; 631 } 632 m_type = Week; 633 return true; 634 } 635 636 bool DateComponents::setWeek(int year, int weekNumber) 637 { 638 m_type = Invalid; 639 if (year < minimumYear() || year > maximumYear()) 640 return false; 641 m_year = year; 642 if (weekNumber < 1 || weekNumber > maxWeekNumberInYear()) 643 return false; 644 m_week = weekNumber; 645 m_type = Week; 646 return true; 647 } 648 649 double 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 655 double 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 676 double DateComponents::monthsSinceEpoch() const 677 { 678 ASSERT(m_type == Month); 679 return (m_year - 1970) * 12 + m_month; 680 } 681 682 String 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 704 String 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 blink 729