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