Rubber-stamped by Brady Eidson.
[webbrowser.git] / WebCore / html / ISODateTime.cpp
blob4c28ac098e244327dc5b4383899549184c3689f5
1 /*
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
6 * met:
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.
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.
31 #include "config.h"
32 #include "ISODateTime.h"
34 #include <limits.h>
35 #include <wtf/ASCIICType.h>
37 namespace WebCore {
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)
48 if (year % 4)
49 return false;
50 if (!(year % 400))
51 return true;
52 if (!(year % 100))
53 return false;
54 return true;
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) {
73 shiftedMonth += 12;
74 year--;
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;
82 return result;
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]))
96 break;
98 return index - start;
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)
105 return false;
106 int value = 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))
113 return false;
114 int digit = *current - '0';
115 if (value > (INT_MAX - digit) / 10) // Check for overflow.
116 return false;
117 value = value * 10 + digit;
119 out = value;
120 return true;
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)
128 return false;
129 int year;
130 if (!toInt(src, length, start, digitsLength, year))
131 return false;
132 // No support for years before Gregorian calendar.
133 if (year < gregorianStartYear)
134 return false;
135 m_year = year;
136 end = start + digitsLength;
137 return true;
140 bool ISODateTime::addDay(int dayDiff)
142 ASSERT(m_monthDay);
144 int day = m_monthDay + dayDiff;
145 if (day > maxDayOfMonth(m_year, m_month)) {
146 day = m_monthDay;
147 int year = m_year;
148 int month = m_month;
149 int maxDay = maxDayOfMonth(year, month);
150 for (; dayDiff > 0; --dayDiff) {
151 ++day;
152 if (day > maxDay) {
153 day = 1;
154 ++month;
155 if (month >= 12) { // month is 0-origin.
156 month = 0;
157 ++year;
158 if (year < 0) // Check for overflow.
159 return false;
161 maxDay = maxDayOfMonth(year, month);
164 m_year = year;
165 m_month = month;
166 } else if (day < 1) {
167 int month = m_month;
168 int year = m_year;
169 day = m_monthDay;
170 for (; dayDiff < 0; ++dayDiff) {
171 --day;
172 if (day < 1) {
173 --month;
174 if (month < 0) {
175 month = 11;
176 --year;
178 day = maxDayOfMonth(year, month);
180 if (year < gregorianStartYear
181 || (year == gregorianStartYear && month < gregorianStartMonth)
182 || (year == gregorianStartYear && month == gregorianStartMonth && day < gregorianStartDay))
183 return false;
185 m_year = year;
186 m_month = month;
188 m_monthDay = day;
189 return true;
192 bool ISODateTime::addMinute(int minute)
194 int carry;
195 // min can be negative or greater than 59.
196 minute += m_minute;
197 if (minute > 59) {
198 carry = minute / 60;
199 minute = minute % 60;
200 } else if (m_minute < 0) {
201 carry = (59 - m_minute) / 60;
202 minute += carry * 60;
203 carry = -carry;
204 ASSERT(minute >= 0 && minute <= 59);
205 } else {
206 m_minute = minute;
207 return true;
210 int hour = m_hour + carry;
211 if (hour > 23) {
212 carry = hour / 24;
213 hour = hour % 24;
214 } else if (hour < 0) {
215 carry = (23 - hour) / 24;
216 hour += carry * 24;
217 carry = -carry;
218 ASSERT(hour >= 0 && hour <= 23);
219 } else {
220 m_minute = minute;
221 m_hour = hour;
222 return true;
224 if (!addDay(carry))
225 return false;
226 m_minute = minute;
227 m_hour = hour;
228 return true;
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)
234 if (start >= length)
235 return false;
236 unsigned index = start;
237 if (src[index] == 'Z') {
238 end = index + 1;
239 return true;
242 bool minus;
243 if (src[index] == '+')
244 minus = false;
245 else if (src[index] == '-')
246 minus = true;
247 else
248 return false;
249 ++index;
251 int hour;
252 int minute;
253 if (!toInt(src, length, index, 2, hour) || hour < 0 || hour > 23)
254 return false;
255 index += 2;
257 if (index >= length || src[index] != ':')
258 return false;
259 ++index;
261 if (!toInt(src, length, index, 2, minute) || minute < 0 || minute > 59)
262 return false;
263 index += 2;
265 if (minus) {
266 hour = -hour;
267 minute = -minute;
270 // Subtract the timezone offset.
271 if (!addMinute(-(hour * 60 + minute)))
272 return false;
273 end = index;
274 return true;
277 bool ISODateTime::parseMonth(const UChar* src, unsigned length, unsigned start, unsigned& end)
279 ASSERT(src);
280 unsigned index;
281 if (!parseYear(src, length, start, index))
282 return false;
283 if (index >= length || src[index] != '-')
284 return false;
285 ++index;
287 int month;
288 if (!toInt(src, length, index, 2, month) || month < 1 || month > 12)
289 return false;
290 --month;
291 // No support for months before Gregorian calendar.
292 if (m_year == gregorianStartYear && month < gregorianStartMonth)
293 return false;
294 m_month = month;
295 end = index + 2;
296 return true;
299 bool ISODateTime::parseDate(const UChar* src, unsigned length, unsigned start, unsigned& end)
301 ASSERT(src);
302 unsigned index;
303 if (!parseMonth(src, length, start, index))
304 return false;
305 // '-' and 2-digits are needed.
306 if (index + 2 >= length)
307 return false;
308 if (src[index] != '-')
309 return false;
310 ++index;
312 int day;
313 if (!toInt(src, length, index, 2, day) || day < 1 || day > maxDayOfMonth(m_year, m_month))
314 return false;
315 // No support for dates before Gregorian calendar.
316 if (m_year == gregorianStartYear && m_month == gregorianStartMonth && day < gregorianStartDay)
317 return false;
318 m_monthDay = day;
319 end = index + 2;
320 return true;
323 bool ISODateTime::parseWeek(const UChar* src, unsigned length, unsigned start, unsigned& end)
325 ASSERT(src);
326 unsigned index;
327 if (!parseYear(src, length, start, index))
328 return false;
330 // 4 characters ('-' 'W' digit digit) are needed.
331 if (index + 3 >= length)
332 return false;
333 if (src[index] != '-')
334 return false;
335 ++index;
336 if (src[index] != 'W')
337 return false;
338 ++index;
340 int week;
341 if (!toInt(src, length, index, 2, week) || week < 1 || week > maxWeekNumberInYear())
342 return false;
343 // No support for years older than or equals to Gregorian calendar start year.
344 if (m_year <= gregorianStartYear)
345 return false;
346 m_week = week;
347 end = index + 2;
348 return true;
351 bool ISODateTime::parseTime(const UChar* src, unsigned length, unsigned start, unsigned& end)
353 ASSERT(src);
354 int hour;
355 if (!toInt(src, length, start, 2, hour) || hour < 0 || hour > 23)
356 return false;
357 unsigned index = start + 2;
358 if (index >= length)
359 return false;
360 if (src[index] != ':')
361 return false;
362 ++index;
364 int minute;
365 if (!toInt(src, length, index, 2, minute) || minute < 0 || minute > 59)
366 return false;
367 index += 2;
369 int second = 0;
370 int millisecond = 0;
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) {
375 index += 3;
377 // Optional fractional second part.
378 if (index < length && src[index] == '.') {
379 unsigned digitsLength = countDigits(src, length, index + 1);
380 if (digitsLength > 0) {
381 ++index;
382 bool ok;
383 if (digitsLength == 1) {
384 ok = toInt(src, length, index, 1, millisecond);
385 millisecond *= 100;
386 } else if (digitsLength == 2) {
387 ok = toInt(src, length, index, 2, millisecond);
388 millisecond *= 10;
389 } else // digitsLength >= 3
390 ok = toInt(src, length, index, 3, millisecond);
391 ASSERT(ok);
392 index += digitsLength;
397 m_hour = hour;
398 m_minute = minute;
399 m_second = second;
400 m_millisecond = millisecond;
401 end = index;
402 return true;
405 bool ISODateTime::parseDateTimeLocal(const UChar* src, unsigned length, unsigned start, unsigned& end)
407 ASSERT(src);
408 unsigned index;
409 if (!parseDate(src, length, start, index))
410 return false;
411 if (index >= length)
412 return false;
413 if (src[index] != 'T')
414 return false;
415 ++index;
416 return parseTime(src, length, index, end);
419 bool ISODateTime::parseDateTime(const UChar* src, unsigned length, unsigned start, unsigned& end)
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, index))
431 return false;
432 return parseTimeZone(src, length, index, end);
435 } // namespace WebCore