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 "third_party/blink/renderer/platform/text/date_components.h"
32
33 #include <limits.h>
34 #include "third_party/blink/renderer/platform/wtf/date_math.h"
35 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
36 #include "third_party/blink/renderer/platform/wtf/text/ascii_ctype.h"
37 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
38
39 namespace blink {
40
41 // HTML5 specification defines minimum week of year is one.
42 const int DateComponents::kMinimumWeekNumber = 1;
43
44 // HTML5 specification defines maximum week of year is 53.
45 const int DateComponents::kMaximumWeekNumber = 53;
46
47 // This is September, since months are 0 based.
48 static const int kMaximumMonthInMaximumYear = 8;
49 static const int kMaximumDayInMaximumMonth = 13;
50 static const int kMaximumWeekInMaximumYear = 37; // The week of 275760-09-13
51
52 static const int kDaysInMonth[12] = {31, 28, 31, 30, 31, 30,
53 31, 31, 30, 31, 30, 31};
54
55 // 'month' is 0-based.
MaxDayOfMonth(int year,int month)56 static int MaxDayOfMonth(int year, int month) {
57 if (month != 1) // February?
58 return kDaysInMonth[month];
59 return IsLeapYear(year) ? 29 : 28;
60 }
61
62 // 'month' is 0-based.
DayOfWeek(int year,int month,int day)63 static int DayOfWeek(int year, int month, int day) {
64 int shifted_month = month + 2;
65 // 2:January, 3:Feburuary, 4:March, ...
66
67 // Zeller's congruence
68 if (shifted_month <= 3) {
69 shifted_month += 12;
70 year--;
71 }
72 // 4:March, ..., 14:January, 15:February
73
74 int high_year = year / 100;
75 int low_year = year % 100;
76 // We add 6 to make the result Sunday-origin.
77 int result = (day + 13 * shifted_month / 5 + low_year + low_year / 4 +
78 high_year / 4 + 5 * high_year + 6) %
79 7;
80 return result;
81 }
82
WeekDay() const83 int DateComponents::WeekDay() const {
84 return DayOfWeek(year_, month_, month_day_);
85 }
86
MaxWeekNumberInYear() const87 int DateComponents::MaxWeekNumberInYear() const {
88 int day = DayOfWeek(year_, 0, 1); // January 1.
89 return day == kThursday || (day == kWednesday && IsLeapYear(year_))
90 ? kMaximumWeekNumber
91 : kMaximumWeekNumber - 1;
92 }
93
CountDigits(const String & src,unsigned start)94 static unsigned CountDigits(const String& src, unsigned start) {
95 unsigned index = start;
96 for (; index < src.length(); ++index) {
97 if (!IsASCIIDigit(src[index]))
98 break;
99 }
100 return index - start;
101 }
102
103 // Very strict integer parser. Do not allow leading or trailing whitespace
104 // unlike charactersToIntStrict().
ToInt(const String & src,unsigned parse_start,unsigned parse_length,int & out)105 static bool ToInt(const String& src,
106 unsigned parse_start,
107 unsigned parse_length,
108 int& out) {
109 if (parse_start + parse_length > src.length() || !parse_length)
110 return false;
111 int value = 0;
112 unsigned current = parse_start;
113 unsigned end = current + parse_length;
114
115 // We don't need to handle negative numbers for ISO 8601.
116 for (; current < end; ++current) {
117 if (!IsASCIIDigit(src[current]))
118 return false;
119 int digit = src[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 String & src,unsigned start,unsigned & end)128 bool DateComponents::ParseYear(const String& src,
129 unsigned start,
130 unsigned& end) {
131 unsigned digits_length = CountDigits(src, start);
132 // Needs at least 4 digits according to the standard.
133 if (digits_length < 4)
134 return false;
135 int year;
136 if (!ToInt(src, start, digits_length, year))
137 return false;
138 if (year < MinimumYear() || year > MaximumYear())
139 return false;
140 year_ = year;
141 end = start + digits_length;
142 return true;
143 }
144
WithinHTMLDateLimits(int year,int month)145 static bool WithinHTMLDateLimits(int year, int month) {
146 if (year < DateComponents::MinimumYear())
147 return false;
148 if (year < DateComponents::MaximumYear())
149 return true;
150 return month <= kMaximumMonthInMaximumYear;
151 }
152
WithinHTMLDateLimits(int year,int month,int month_day)153 static bool WithinHTMLDateLimits(int year, int month, int month_day) {
154 if (year < DateComponents::MinimumYear())
155 return false;
156 if (year < DateComponents::MaximumYear())
157 return true;
158 if (month < kMaximumMonthInMaximumYear)
159 return true;
160 return month_day <= kMaximumDayInMaximumMonth;
161 }
162
WithinHTMLDateLimits(int year,int month,int month_day,int hour,int minute,int second,int millisecond)163 static bool WithinHTMLDateLimits(int year,
164 int month,
165 int month_day,
166 int hour,
167 int minute,
168 int second,
169 int millisecond) {
170 if (year < DateComponents::MinimumYear())
171 return false;
172 if (year < DateComponents::MaximumYear())
173 return true;
174 if (month < kMaximumMonthInMaximumYear)
175 return true;
176 if (month_day < kMaximumDayInMaximumMonth)
177 return true;
178 if (month_day > kMaximumDayInMaximumMonth)
179 return false;
180 // (year, month, monthDay) =
181 // (MaximumYear, kMaximumMonthInMaximumYear, kMaximumDayInMaximumMonth)
182 return !hour && !minute && !second && !millisecond;
183 }
184
ParseMonth(const String & src,unsigned start,unsigned & end)185 bool DateComponents::ParseMonth(const String& src,
186 unsigned start,
187 unsigned& end) {
188 unsigned index;
189 if (!ParseYear(src, start, index))
190 return false;
191 if (index >= src.length() || src[index] != '-')
192 return false;
193 ++index;
194
195 int month;
196 if (!ToInt(src, index, 2, month) || month < 1 || month > 12)
197 return false;
198 --month;
199 if (!WithinHTMLDateLimits(year_, month))
200 return false;
201 month_ = month;
202 end = index + 2;
203 type_ = kMonth;
204 return true;
205 }
206
ParseDate(const String & src,unsigned start,unsigned & end)207 bool DateComponents::ParseDate(const String& src,
208 unsigned start,
209 unsigned& end) {
210 unsigned index;
211 if (!ParseMonth(src, start, index))
212 return false;
213 // '-' and 2-digits are needed.
214 if (index + 2 >= src.length())
215 return false;
216 if (src[index] != '-')
217 return false;
218 ++index;
219
220 int day;
221 if (!ToInt(src, index, 2, day) || day < 1 ||
222 day > MaxDayOfMonth(year_, month_))
223 return false;
224 if (!WithinHTMLDateLimits(year_, month_, day))
225 return false;
226 month_day_ = day;
227 end = index + 2;
228 type_ = kDate;
229 return true;
230 }
231
ParseWeek(const String & src,unsigned start,unsigned & end)232 bool DateComponents::ParseWeek(const String& src,
233 unsigned start,
234 unsigned& end) {
235 unsigned index;
236 if (!ParseYear(src, start, index))
237 return false;
238
239 // 4 characters ('-' 'W' digit digit) are needed.
240 if (index + 3 >= src.length())
241 return false;
242 if (src[index] != '-')
243 return false;
244 ++index;
245 if (src[index] != 'W')
246 return false;
247 ++index;
248
249 int week;
250 if (!ToInt(src, index, 2, week) || week < kMinimumWeekNumber ||
251 week > MaxWeekNumberInYear())
252 return false;
253 if (year_ == MaximumYear() && week > kMaximumWeekInMaximumYear)
254 return false;
255 week_ = week;
256 end = index + 2;
257 type_ = kWeek;
258 return true;
259 }
260
ParseTime(const String & src,unsigned start,unsigned & end)261 bool DateComponents::ParseTime(const String& src,
262 unsigned start,
263 unsigned& end) {
264 int hour;
265 if (!ToInt(src, start, 2, hour) || hour < 0 || hour > 23)
266 return false;
267 unsigned index = start + 2;
268 if (index >= src.length())
269 return false;
270 if (src[index] != ':')
271 return false;
272 ++index;
273
274 int minute;
275 if (!ToInt(src, index, 2, minute) || minute < 0 || minute > 59)
276 return false;
277 index += 2;
278
279 int second = 0;
280 int millisecond = 0;
281 // Optional second part.
282 // Do not return with false because the part is optional.
283 if (index + 2 < src.length() && src[index] == ':') {
284 if (ToInt(src, index + 1, 2, second) && second >= 0 && second <= 59) {
285 index += 3;
286
287 // Optional fractional second part.
288 if (index < src.length() && src[index] == '.') {
289 unsigned digits_length = CountDigits(src, index + 1);
290 if (digits_length > 0) {
291 ++index;
292 bool ok;
293 if (digits_length == 1) {
294 ok = ToInt(src, index, 1, millisecond);
295 millisecond *= 100;
296 } else if (digits_length == 2) {
297 ok = ToInt(src, index, 2, millisecond);
298 millisecond *= 10;
299 } else if (digits_length == 3) {
300 ok = ToInt(src, index, 3, millisecond);
301 } else { // digits_length >= 4
302 return false;
303 }
304 DCHECK(ok);
305 index += digits_length;
306 }
307 }
308 }
309 }
310 hour_ = hour;
311 minute_ = minute;
312 second_ = second;
313 millisecond_ = millisecond;
314 end = index;
315 type_ = kTime;
316 return true;
317 }
318
ParseDateTimeLocal(const String & src,unsigned start,unsigned & end)319 bool DateComponents::ParseDateTimeLocal(const String& src,
320 unsigned start,
321 unsigned& end) {
322 unsigned index;
323 if (!ParseDate(src, start, index))
324 return false;
325 if (index >= src.length())
326 return false;
327 if (src[index] != 'T')
328 return false;
329 ++index;
330 if (!ParseTime(src, index, end))
331 return false;
332 if (!WithinHTMLDateLimits(year_, month_, month_day_, hour_, minute_, second_,
333 millisecond_))
334 return false;
335 type_ = kDateTimeLocal;
336 return true;
337 }
338
PositiveFmod(double value,double divider)339 static inline double PositiveFmod(double value, double divider) {
340 double remainder = fmod(value, divider);
341 return remainder < 0 ? remainder + divider : remainder;
342 }
343
SetMillisecondsSinceMidnightInternal(double ms_in_day)344 void DateComponents::SetMillisecondsSinceMidnightInternal(double ms_in_day) {
345 DCHECK_GE(ms_in_day, 0);
346 DCHECK_LT(ms_in_day, kMsPerDay);
347 millisecond_ = static_cast<int>(fmod(ms_in_day, kMsPerSecond));
348 double value = std::floor(ms_in_day / kMsPerSecond);
349 second_ = static_cast<int>(fmod(value, kSecondsPerMinute));
350 value = std::floor(value / kSecondsPerMinute);
351 minute_ = static_cast<int>(fmod(value, kMinutesPerHour));
352 hour_ = static_cast<int>(value / kMinutesPerHour);
353 }
354
SetMillisecondsSinceEpochForDateInternal(double ms)355 bool DateComponents::SetMillisecondsSinceEpochForDateInternal(double ms) {
356 year_ = MsToYear(ms);
357 int year_day = DayInYear(ms, year_);
358 month_ = MonthFromDayInYear(year_day, IsLeapYear(year_));
359 month_day_ = DayInMonthFromDayInYear(year_day, IsLeapYear(year_));
360 return true;
361 }
362
SetMillisecondsSinceEpochForDate(double ms)363 bool DateComponents::SetMillisecondsSinceEpochForDate(double ms) {
364 type_ = kInvalid;
365 if (!std::isfinite(ms))
366 return false;
367 if (!SetMillisecondsSinceEpochForDateInternal(round(ms)))
368 return false;
369 if (!WithinHTMLDateLimits(year_, month_, month_day_))
370 return false;
371 type_ = kDate;
372 return true;
373 }
374
SetMillisecondsSinceEpochForDateTimeLocal(double ms)375 bool DateComponents::SetMillisecondsSinceEpochForDateTimeLocal(double ms) {
376 type_ = kInvalid;
377 if (!std::isfinite(ms))
378 return false;
379 ms = round(ms);
380 SetMillisecondsSinceMidnightInternal(PositiveFmod(ms, kMsPerDay));
381 if (!SetMillisecondsSinceEpochForDateInternal(ms))
382 return false;
383 if (!WithinHTMLDateLimits(year_, month_, month_day_, hour_, minute_, second_,
384 millisecond_))
385 return false;
386 type_ = kDateTimeLocal;
387 return true;
388 }
389
SetMillisecondsSinceEpochForMonth(double ms)390 bool DateComponents::SetMillisecondsSinceEpochForMonth(double ms) {
391 type_ = kInvalid;
392 if (!std::isfinite(ms))
393 return false;
394 if (!SetMillisecondsSinceEpochForDateInternal(round(ms)))
395 return false;
396 if (!WithinHTMLDateLimits(year_, month_))
397 return false;
398 type_ = kMonth;
399 return true;
400 }
401
SetMillisecondsSinceMidnight(double ms)402 bool DateComponents::SetMillisecondsSinceMidnight(double ms) {
403 type_ = kInvalid;
404 if (!std::isfinite(ms))
405 return false;
406 SetMillisecondsSinceMidnightInternal(PositiveFmod(round(ms), kMsPerDay));
407 type_ = kTime;
408 return true;
409 }
410
SetMonthsSinceEpoch(double months)411 bool DateComponents::SetMonthsSinceEpoch(double months) {
412 if (!std::isfinite(months))
413 return false;
414 months = round(months);
415 double double_month = PositiveFmod(months, 12);
416 double double_year = 1970 + (months - double_month) / 12;
417 if (double_year < MinimumYear() || MaximumYear() < double_year)
418 return false;
419 int year = static_cast<int>(double_year);
420 int month = static_cast<int>(double_month);
421 if (!WithinHTMLDateLimits(year, month))
422 return false;
423 year_ = year;
424 month_ = month;
425 type_ = kMonth;
426 return true;
427 }
428
429 // Offset from January 1st to Monday of the ISO 8601's first week.
430 // ex. If January 1st is Friday, such Monday is 3 days later. Returns 3.
OffsetTo1stWeekStart(int year)431 static int OffsetTo1stWeekStart(int year) {
432 int offset_to1st_week_start = 1 - DayOfWeek(year, 0, 1);
433 if (offset_to1st_week_start <= -4)
434 offset_to1st_week_start += 7;
435 return offset_to1st_week_start;
436 }
437
SetMillisecondsSinceEpochForWeek(double ms)438 bool DateComponents::SetMillisecondsSinceEpochForWeek(double ms) {
439 type_ = kInvalid;
440 if (!std::isfinite(ms))
441 return false;
442 ms = round(ms);
443
444 year_ = MsToYear(ms);
445 if (year_ < MinimumYear() || year_ > MaximumYear())
446 return false;
447
448 int year_day = DayInYear(ms, year_);
449 int offset = OffsetTo1stWeekStart(year_);
450 if (year_day < offset) {
451 // The day belongs to the last week of the previous year.
452 year_--;
453 if (year_ <= MinimumYear())
454 return false;
455 week_ = MaxWeekNumberInYear();
456 } else {
457 week_ = ((year_day - offset) / 7) + 1;
458 if (week_ > MaxWeekNumberInYear()) {
459 year_++;
460 week_ = 1;
461 }
462 if (year_ > MaximumYear() ||
463 (year_ == MaximumYear() && week_ > kMaximumWeekInMaximumYear))
464 return false;
465 }
466 type_ = kWeek;
467 return true;
468 }
469
SetWeek(int year,int week_number)470 bool DateComponents::SetWeek(int year, int week_number) {
471 type_ = kInvalid;
472 if (year < MinimumYear() || year > MaximumYear())
473 return false;
474 year_ = year;
475 if (week_number < 1 || week_number > MaxWeekNumberInYear())
476 return false;
477 week_ = week_number;
478 type_ = kWeek;
479 return true;
480 }
481
MillisecondsSinceEpochForTime() const482 double DateComponents::MillisecondsSinceEpochForTime() const {
483 DCHECK(type_ == kTime || type_ == kDateTimeLocal);
484 return ((hour_ * kMinutesPerHour + minute_) * kSecondsPerMinute + second_) *
485 kMsPerSecond +
486 millisecond_;
487 }
488
MillisecondsSinceEpoch() const489 double DateComponents::MillisecondsSinceEpoch() const {
490 switch (type_) {
491 case kDate:
492 return DateToDaysFrom1970(year_, month_, month_day_) * kMsPerDay;
493 case kDateTimeLocal:
494 return DateToDaysFrom1970(year_, month_, month_day_) * kMsPerDay +
495 MillisecondsSinceEpochForTime();
496 case kMonth:
497 return DateToDaysFrom1970(year_, month_, 1) * kMsPerDay;
498 case kTime:
499 return MillisecondsSinceEpochForTime();
500 case kWeek:
501 return (DateToDaysFrom1970(year_, 0, 1) + OffsetTo1stWeekStart(year_) +
502 (week_ - 1) * 7) *
503 kMsPerDay;
504 case kInvalid:
505 break;
506 }
507 NOTREACHED();
508 return InvalidMilliseconds();
509 }
510
MonthsSinceEpoch() const511 double DateComponents::MonthsSinceEpoch() const {
512 DCHECK_EQ(type_, kMonth);
513 return (year_ - 1970) * 12 + month_;
514 }
515
ToStringForTime(SecondFormat format) const516 String DateComponents::ToStringForTime(SecondFormat format) const {
517 DCHECK(type_ == kDateTimeLocal || type_ == kTime);
518 SecondFormat effective_format = format;
519 if (millisecond_)
520 effective_format = kMillisecond;
521 else if (format == kNone && second_)
522 effective_format = kSecond;
523
524 switch (effective_format) {
525 default:
526 NOTREACHED();
527 FALLTHROUGH;
528 case kNone:
529 return String::Format("%02d:%02d", hour_, minute_);
530 case kSecond:
531 return String::Format("%02d:%02d:%02d", hour_, minute_, second_);
532 case kMillisecond:
533 return String::Format("%02d:%02d:%02d.%03d", hour_, minute_, second_,
534 millisecond_);
535 }
536 }
537
ToString(SecondFormat format) const538 String DateComponents::ToString(SecondFormat format) const {
539 switch (type_) {
540 case kDate:
541 return String::Format("%04d-%02d-%02d", year_, month_ + 1, month_day_);
542 case kDateTimeLocal:
543 return String::Format("%04d-%02d-%02dT", year_, month_ + 1, month_day_) +
544 ToStringForTime(format);
545 case kMonth:
546 return String::Format("%04d-%02d", year_, month_ + 1);
547 case kTime:
548 return ToStringForTime(format);
549 case kWeek:
550 return String::Format("%04d-W%02d", year_, week_);
551 case kInvalid:
552 break;
553 }
554 NOTREACHED();
555 return String("(Invalid DateComponents)");
556 }
557
558 } // namespace blink
559