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