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