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