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 "ISODateTime.h"
35 #include <wtf/ASCIICType.h>
39 // The oldest day of Gregorian Calendar is 1582-10-15. We don't support dates older than it.
40 static const int gregorianStartYear
= 1582;
41 static const int gregorianStartMonth
= 9; // This is October, since months are 0 based.
42 static const int gregorianStartDay
= 15;
44 static const int daysInMonth
[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
46 static bool isLeapYear(int year
)
57 // `month' is 0-based.
58 static int maxDayOfMonth(int year
, int month
)
60 if (month
!= 1) // February?
61 return daysInMonth
[month
];
62 return isLeapYear(year
) ? 29 : 28;
65 // `month' is 0-based.
66 static int dayOfWeek(int year
, int month
, int day
)
68 int shiftedMonth
= month
+ 2;
69 // 2:January, 3:Feburuary, 4:March, ...
71 // Zeller's congruence
72 if (shiftedMonth
<= 3) {
76 // 4:March, ..., 14:January, 15:February
78 int highYear
= year
/ 100;
79 int lowYear
= year
% 100;
80 // We add 6 to make the result Sunday-origin.
81 int result
= (day
+ 13 * shiftedMonth
/ 5 + lowYear
+ lowYear
/ 4 + highYear
/ 4 + 5 * highYear
+ 6) % 7;
85 int ISODateTime::maxWeekNumberInYear() const
87 int day
= dayOfWeek(m_year
, 0, 1); // January 1.
88 return day
== Thursday
|| (day
== Wednesday
&& isLeapYear(m_year
)) ? 53 : 52;
91 static unsigned countDigits(const UChar
* src
, unsigned length
, unsigned start
)
93 unsigned index
= start
;
94 for (; index
< length
; ++index
) {
95 if (!isASCIIDigit(src
[index
]))
101 // Very strict integer parser. Do not allow leading or trailing whitespace unlike charactersToIntStrict().
102 static bool toInt(const UChar
* src
, unsigned length
, unsigned parseStart
, unsigned parseLength
, int& out
)
104 if (parseStart
+ parseLength
> length
|| parseLength
<= 0)
107 const UChar
* current
= src
+ parseStart
;
108 const UChar
* end
= current
+ parseLength
;
110 // We don't need to handle negative numbers for ISO 8601.
111 for (; current
< end
; ++current
) {
112 if (!isASCIIDigit(*current
))
114 int digit
= *current
- '0';
115 if (value
> (INT_MAX
- digit
) / 10) // Check for overflow.
117 value
= value
* 10 + digit
;
123 bool ISODateTime::parseYear(const UChar
* src
, unsigned length
, unsigned start
, unsigned& end
)
125 unsigned digitsLength
= countDigits(src
, length
, start
);
126 // Needs at least 4 digits according to the standard.
127 if (digitsLength
< 4)
130 if (!toInt(src
, length
, start
, digitsLength
, year
))
132 // No support for years before Gregorian calendar.
133 if (year
< gregorianStartYear
)
136 end
= start
+ digitsLength
;
140 bool ISODateTime::addDay(int dayDiff
)
144 int day
= m_monthDay
+ dayDiff
;
145 if (day
> maxDayOfMonth(m_year
, m_month
)) {
149 int maxDay
= maxDayOfMonth(year
, month
);
150 for (; dayDiff
> 0; --dayDiff
) {
155 if (month
>= 12) { // month is 0-origin.
158 if (year
< 0) // Check for overflow.
161 maxDay
= maxDayOfMonth(year
, month
);
166 } else if (day
< 1) {
170 for (; dayDiff
< 0; ++dayDiff
) {
178 day
= maxDayOfMonth(year
, month
);
180 if (year
< gregorianStartYear
181 || (year
== gregorianStartYear
&& month
< gregorianStartMonth
)
182 || (year
== gregorianStartYear
&& month
== gregorianStartMonth
&& day
< gregorianStartDay
))
192 bool ISODateTime::addMinute(int minute
)
195 // min can be negative or greater than 59.
199 minute
= minute
% 60;
200 } else if (m_minute
< 0) {
201 carry
= (59 - m_minute
) / 60;
202 minute
+= carry
* 60;
204 ASSERT(minute
>= 0 && minute
<= 59);
210 int hour
= m_hour
+ carry
;
214 } else if (hour
< 0) {
215 carry
= (23 - hour
) / 24;
218 ASSERT(hour
>= 0 && hour
<= 23);
231 // Parses a timezone part, and adjust year, month, monthDay, hour, minute, second, millisecond.
232 bool ISODateTime::parseTimeZone(const UChar
* src
, unsigned length
, unsigned start
, unsigned& end
)
236 unsigned index
= start
;
237 if (src
[index
] == 'Z') {
243 if (src
[index
] == '+')
245 else if (src
[index
] == '-')
253 if (!toInt(src
, length
, index
, 2, hour
) || hour
< 0 || hour
> 23)
257 if (index
>= length
|| src
[index
] != ':')
261 if (!toInt(src
, length
, index
, 2, minute
) || minute
< 0 || minute
> 59)
270 // Subtract the timezone offset.
271 if (!addMinute(-(hour
* 60 + minute
)))
277 bool ISODateTime::parseMonth(const UChar
* src
, unsigned length
, unsigned start
, unsigned& end
)
281 if (!parseYear(src
, length
, start
, index
))
283 if (index
>= length
|| src
[index
] != '-')
288 if (!toInt(src
, length
, index
, 2, month
) || month
< 1 || month
> 12)
291 // No support for months before Gregorian calendar.
292 if (m_year
== gregorianStartYear
&& month
< gregorianStartMonth
)
299 bool ISODateTime::parseDate(const UChar
* src
, unsigned length
, unsigned start
, unsigned& end
)
303 if (!parseMonth(src
, length
, start
, index
))
305 // '-' and 2-digits are needed.
306 if (index
+ 2 >= length
)
308 if (src
[index
] != '-')
313 if (!toInt(src
, length
, index
, 2, day
) || day
< 1 || day
> maxDayOfMonth(m_year
, m_month
))
315 // No support for dates before Gregorian calendar.
316 if (m_year
== gregorianStartYear
&& m_month
== gregorianStartMonth
&& day
< gregorianStartDay
)
323 bool ISODateTime::parseWeek(const UChar
* src
, unsigned length
, unsigned start
, unsigned& end
)
327 if (!parseYear(src
, length
, start
, index
))
330 // 4 characters ('-' 'W' digit digit) are needed.
331 if (index
+ 3 >= length
)
333 if (src
[index
] != '-')
336 if (src
[index
] != 'W')
341 if (!toInt(src
, length
, index
, 2, week
) || week
< 1 || week
> maxWeekNumberInYear())
343 // No support for years older than or equals to Gregorian calendar start year.
344 if (m_year
<= gregorianStartYear
)
351 bool ISODateTime::parseTime(const UChar
* src
, unsigned length
, unsigned start
, unsigned& end
)
355 if (!toInt(src
, length
, start
, 2, hour
) || hour
< 0 || hour
> 23)
357 unsigned index
= start
+ 2;
360 if (src
[index
] != ':')
365 if (!toInt(src
, length
, index
, 2, minute
) || minute
< 0 || minute
> 59)
371 // Optional second part.
372 // Do not return with false because the part is optional.
373 if (index
+ 2 < length
&& src
[index
] == ':') {
374 if (toInt(src
, length
, index
+ 1, 2, second
) && second
>= 0 && second
<= 59) {
377 // Optional fractional second part.
378 if (index
< length
&& src
[index
] == '.') {
379 unsigned digitsLength
= countDigits(src
, length
, index
+ 1);
380 if (digitsLength
> 0) {
383 if (digitsLength
== 1) {
384 ok
= toInt(src
, length
, index
, 1, millisecond
);
386 } else if (digitsLength
== 2) {
387 ok
= toInt(src
, length
, index
, 2, millisecond
);
389 } else // digitsLength >= 3
390 ok
= toInt(src
, length
, index
, 3, millisecond
);
392 index
+= digitsLength
;
400 m_millisecond
= millisecond
;
405 bool ISODateTime::parseDateTimeLocal(const UChar
* src
, unsigned length
, unsigned start
, unsigned& end
)
409 if (!parseDate(src
, length
, start
, index
))
413 if (src
[index
] != 'T')
416 return parseTime(src
, length
, index
, end
);
419 bool ISODateTime::parseDateTime(const UChar
* src
, unsigned length
, unsigned start
, unsigned& end
)
423 if (!parseDate(src
, length
, start
, index
))
427 if (src
[index
] != 'T')
430 if (!parseTime(src
, length
, index
, index
))
432 return parseTimeZone(src
, length
, index
, end
);
435 } // namespace WebCore