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