2 * Copyright (C) 2009 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "platform/DateComponents.h"
35 #include "wtf/ASCIICType.h"
36 #include "wtf/DateMath.h"
37 #include "wtf/MathExtras.h"
38 #include "wtf/text/WTFString.h"
44 // HTML5 specification defines minimum week of year is one.
45 const int DateComponents::minimumWeekNumber = 1;
47 // HTML5 specification defines maximum week of year is 53.
48 const int DateComponents::maximumWeekNumber = 53;
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
54 static const int daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
56 // 'month' is 0-based.
57 static int maxDayOfMonth(int year, int month)
59 if (month != 1) // February?
60 return daysInMonth[month];
61 return isLeapYear(year) ? 29 : 28;
64 // 'month' is 0-based.
65 static int dayOfWeek(int year, int month, int day)
67 int shiftedMonth = month + 2;
68 // 2:January, 3:Feburuary, 4:March, ...
70 // Zeller's congruence
71 if (shiftedMonth <= 3) {
75 // 4:March, ..., 14:January, 15:February
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;
84 int DateComponents::weekDay() const
86 return dayOfWeek(m_year, m_month, m_monthDay);
89 int DateComponents::maxWeekNumberInYear() const
91 int day = dayOfWeek(m_year, 0, 1); // January 1.
92 return day == Thursday || (day == Wednesday && isLeapYear(m_year)) ? maximumWeekNumber : maximumWeekNumber - 1;
95 static unsigned countDigits(const String& src, unsigned start)
97 unsigned index = start;
98 for (; index < src.length(); ++index) {
99 if (!isASCIIDigit(src[index]))
102 return index - start;
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)
108 if (parseStart + parseLength > src.length() || !parseLength)
111 unsigned current = parseStart;
112 unsigned end = current + parseLength;
114 // We don't need to handle negative numbers for ISO 8601.
115 for (; current < end; ++current) {
116 if (!isASCIIDigit(src[current]))
118 int digit = src[current] - '0';
119 if (value > (INT_MAX - digit) / 10) // Check for overflow.
121 value = value * 10 + digit;
127 bool DateComponents::parseYear(const String& src, unsigned start, unsigned& end)
129 unsigned digitsLength = countDigits(src, start);
130 // Needs at least 4 digits according to the standard.
131 if (digitsLength < 4)
134 if (!toInt(src, start, digitsLength, year))
136 if (year < minimumYear() || year > maximumYear())
139 end = start + digitsLength;
143 static bool withinHTMLDateLimits(int year, int month)
145 if (year < DateComponents::minimumYear())
147 if (year < DateComponents::maximumYear())
149 return month <= maximumMonthInMaximumYear;
152 static bool withinHTMLDateLimits(int year, int month, int monthDay)
154 if (year < DateComponents::minimumYear())
156 if (year < DateComponents::maximumYear())
158 if (month < maximumMonthInMaximumYear)
160 return monthDay <= maximumDayInMaximumMonth;
163 static bool withinHTMLDateLimits(int year, int month, int monthDay, int hour, int minute, int second, int millisecond)
165 if (year < DateComponents::minimumYear())
167 if (year < DateComponents::maximumYear())
169 if (month < maximumMonthInMaximumYear)
171 if (monthDay < maximumDayInMaximumMonth)
173 if (monthDay > maximumDayInMaximumMonth)
175 // (year, month, monthDay) = (maximumYear, maximumMonthInMaximumYear, maximumDayInMaximumMonth)
176 return !hour && !minute && !second && !millisecond;
179 bool DateComponents::addDay(int dayDiff)
183 int day = m_monthDay + dayDiff;
184 if (day > maxDayOfMonth(m_year, m_month)) {
188 int maxDay = maxDayOfMonth(year, month);
189 for (; dayDiff > 0; --dayDiff) {
194 if (month >= 12) { // month is 0-origin.
198 maxDay = maxDayOfMonth(year, month);
201 if (!withinHTMLDateLimits(year, month, day))
205 } else if (day < 1) {
209 for (; dayDiff < 0; ++dayDiff) {
217 day = maxDayOfMonth(year, month);
220 if (!withinHTMLDateLimits(year, month, day))
225 if (!withinHTMLDateLimits(m_year, m_month, day))
232 bool DateComponents::addMinute(int minute)
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));
239 // minute can be negative or greater than 59.
243 minute = minute % 60;
244 } else if (minute < 0) {
245 carry = (59 - minute) / 60;
246 minute += carry * 60;
248 ASSERT(minute >= 0 && minute <= 59);
250 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, minute, m_second, m_millisecond))
256 int hour = m_hour + carry;
260 } else if (hour < 0) {
261 carry = (23 - hour) / 24;
264 ASSERT(hour >= 0 && hour <= 23);
266 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, hour, minute, m_second, m_millisecond))
274 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, hour, minute, m_second, m_millisecond))
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)
284 if (start >= src.length())
286 unsigned index = start;
287 if (src[index] == 'Z') {
293 if (src[index] == '+')
295 else if (src[index] == '-')
303 if (!toInt(src, index, 2, hour) || hour < 0 || hour > 23)
307 if (index >= src.length() || src[index] != ':')
311 if (!toInt(src, index, 2, minute) || minute < 0 || minute > 59)
320 // Subtract the timezone offset.
321 if (!addMinute(-(hour * 60 + minute)))
327 bool DateComponents::parseMonth(const String& src, unsigned start, unsigned& end)
330 if (!parseYear(src, start, index))
332 if (index >= src.length() || src[index] != '-')
337 if (!toInt(src, index, 2, month) || month < 1 || month > 12)
340 if (!withinHTMLDateLimits(m_year, month))
348 bool DateComponents::parseDate(const String& src, unsigned start, unsigned& end)
351 if (!parseMonth(src, start, index))
353 // '-' and 2-digits are needed.
354 if (index + 2 >= src.length())
356 if (src[index] != '-')
361 if (!toInt(src, index, 2, day) || day < 1 || day > maxDayOfMonth(m_year, m_month))
363 if (!withinHTMLDateLimits(m_year, m_month, day))
371 bool DateComponents::parseWeek(const String& src, unsigned start, unsigned& end)
374 if (!parseYear(src, start, index))
377 // 4 characters ('-' 'W' digit digit) are needed.
378 if (index + 3 >= src.length())
380 if (src[index] != '-')
383 if (src[index] != 'W')
388 if (!toInt(src, index, 2, week) || week < minimumWeekNumber || week > maxWeekNumberInYear())
390 if (m_year == maximumYear() && week > maximumWeekInMaximumYear)
398 bool DateComponents::parseTime(const String& src, unsigned start, unsigned& end)
401 if (!toInt(src, start, 2, hour) || hour < 0 || hour > 23)
403 unsigned index = start + 2;
404 if (index >= src.length())
406 if (src[index] != ':')
411 if (!toInt(src, index, 2, minute) || minute < 0 || minute > 59)
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) {
423 // Optional fractional second part.
424 if (index < src.length() && src[index] == '.') {
425 unsigned digitsLength = countDigits(src, index + 1);
426 if (digitsLength > 0) {
429 if (digitsLength == 1) {
430 ok = toInt(src, index, 1, millisecond);
432 } else if (digitsLength == 2) {
433 ok = toInt(src, index, 2, millisecond);
435 } else { // digitsLength >= 3
436 ok = toInt(src, index, 3, millisecond);
438 ASSERT_UNUSED(ok, ok);
439 index += digitsLength;
447 m_millisecond = millisecond;
453 bool DateComponents::parseDateTimeLocal(const String& src, unsigned start, unsigned& end)
456 if (!parseDate(src, start, index))
458 if (index >= src.length())
460 if (src[index] != 'T')
463 if (!parseTime(src, index, end))
465 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond))
467 m_type = DateTimeLocal;
471 bool DateComponents::parseDateTime(const String& src, unsigned start, unsigned& end)
474 if (!parseDate(src, start, index))
476 if (index >= src.length())
478 if (src[index] != 'T')
481 if (!parseTime(src, index, index))
483 if (!parseTimeZone(src, index, end))
485 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond))
491 static inline double positiveFmod(double value, double divider)
493 double remainder = fmod(value, divider);
494 return remainder < 0 ? remainder + divider : remainder;
497 void DateComponents::setMillisecondsSinceMidnightInternal(double msInDay)
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);
508 bool DateComponents::setMillisecondsSinceEpochForDateInternal(double ms)
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));
517 bool DateComponents::setMillisecondsSinceEpochForDate(double ms)
520 if (!std::isfinite(ms))
522 if (!setMillisecondsSinceEpochForDateInternal(round(ms)))
524 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay))
530 bool DateComponents::setMillisecondsSinceEpochForDateTime(double ms)
533 if (!std::isfinite(ms))
536 setMillisecondsSinceMidnightInternal(positiveFmod(ms, msPerDay));
537 if (!setMillisecondsSinceEpochForDateInternal(ms))
539 if (!withinHTMLDateLimits(m_year, m_month, m_monthDay, m_hour, m_minute, m_second, m_millisecond))
545 bool DateComponents::setMillisecondsSinceEpochForDateTimeLocal(double ms)
547 // Internal representation of DateTimeLocal is the same as DateTime except m_type.
548 if (!setMillisecondsSinceEpochForDateTime(ms))
550 m_type = DateTimeLocal;
554 bool DateComponents::setMillisecondsSinceEpochForMonth(double ms)
557 if (!std::isfinite(ms))
559 if (!setMillisecondsSinceEpochForDateInternal(round(ms)))
561 if (!withinHTMLDateLimits(m_year, m_month))
567 bool DateComponents::setMillisecondsSinceMidnight(double ms)
570 if (!std::isfinite(ms))
572 setMillisecondsSinceMidnightInternal(positiveFmod(round(ms), msPerDay));
577 bool DateComponents::setMonthsSinceEpoch(double months)
579 if (!std::isfinite(months))
581 months = round(months);
582 double doubleMonth = positiveFmod(months, 12);
583 double doubleYear = 1970 + (months - doubleMonth) / 12;
584 if (doubleYear < minimumYear() || maximumYear() < doubleYear)
586 int year = static_cast<int>(doubleYear);
587 int month = static_cast<int>(doubleMonth);
588 if (!withinHTMLDateLimits(year, month))
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)
600 int offsetTo1stWeekStart = 1 - dayOfWeek(year, 0, 1);
601 if (offsetTo1stWeekStart <= -4)
602 offsetTo1stWeekStart += 7;
603 return offsetTo1stWeekStart;
606 bool DateComponents::setMillisecondsSinceEpochForWeek(double ms)
609 if (!std::isfinite(ms))
613 m_year = msToYear(ms);
614 if (m_year < minimumYear() || m_year > maximumYear())
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.
622 if (m_year <= minimumYear())
624 m_week = maxWeekNumberInYear();
626 m_week = ((yearDay - offset) / 7) + 1;
627 if (m_week > maxWeekNumberInYear()) {
631 if (m_year > maximumYear() || (m_year == maximumYear() && m_week > maximumWeekInMaximumYear))
638 double DateComponents::millisecondsSinceEpochForTime() const
640 ASSERT(m_type == Time || m_type == DateTime || m_type == DateTimeLocal);
641 return ((m_hour * minutesPerHour + m_minute) * secondsPerMinute + m_second) * msPerSecond + m_millisecond;
644 double DateComponents::millisecondsSinceEpoch() const
648 return dateToDaysFrom1970(m_year, m_month, m_monthDay) * msPerDay;
651 return dateToDaysFrom1970(m_year, m_month, m_monthDay) * msPerDay + millisecondsSinceEpochForTime();
653 return dateToDaysFrom1970(m_year, m_month, 1) * msPerDay;
655 return millisecondsSinceEpochForTime();
657 return (dateToDaysFrom1970(m_year, 0, 1) + offsetTo1stWeekStart(m_year) + (m_week - 1) * 7) * msPerDay;
661 ASSERT_NOT_REACHED();
662 return invalidMilliseconds();
665 double DateComponents::monthsSinceEpoch() const
667 ASSERT(m_type == Month);
668 return (m_year - 1970) * 12 + m_month;
671 String DateComponents::toStringForTime(SecondFormat format) const
673 ASSERT(m_type == DateTime || m_type == DateTimeLocal || m_type == Time);
674 SecondFormat effectiveFormat = format;
676 effectiveFormat = Millisecond;
677 else if (format == None && m_second)
678 effectiveFormat = Second;
680 switch (effectiveFormat) {
682 ASSERT_NOT_REACHED();
685 return String::format("%02d:%02d", m_hour, m_minute);
687 return String::format("%02d:%02d:%02d", m_hour, m_minute, m_second);
689 return String::format("%02d:%02d:%02d.%03d", m_hour, m_minute, m_second, m_millisecond);
693 String DateComponents::toString(SecondFormat format) const
697 return String::format("%04d-%02d-%02d", m_year, m_month + 1, m_monthDay);
699 return String::format("%04d-%02d-%02dT", m_year, m_month + 1, m_monthDay)
700 + toStringForTime(format) + String("Z");
702 return String::format("%04d-%02d-%02dT", m_year, m_month + 1, m_monthDay)
703 + toStringForTime(format);
705 return String::format("%04d-%02d", m_year, m_month + 1);
707 return toStringForTime(format);
709 return String::format("%04d-W%02d", m_year, m_week);
713 ASSERT_NOT_REACHED();
714 return String("(Invalid DateComponents)");