Fix "Dead nested assignment" static analyzer warning in monthFromDayInYear()
[WebKit.git] / Source / WTF / wtf / DateMath.h
1 /*
2  * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  * Copyright (C) 2006-2020 Apple Inc. All rights reserved.
4  * Copyright (C) 2009 Google Inc. All rights reserved.
5  * Copyright (C) 2010 Research In Motion Limited. All rights reserved.
6  *
7  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
8  *
9  * The contents of this file are subject to the Mozilla Public License Version
10  * 1.1 (the "License"); you may not use this file except in compliance with
11  * the License. You may obtain a copy of the License at
12  * http://www.mozilla.org/MPL/
13  *
14  * Software distributed under the License is distributed on an "AS IS" basis,
15  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16  * for the specific language governing rights and limitations under the
17  * License.
18  *
19  * The Original Code is Mozilla Communicator client code, released
20  * March 31, 1998.
21  *
22  * The Initial Developer of the Original Code is
23  * Netscape Communications Corporation.
24  * Portions created by the Initial Developer are Copyright (C) 1998
25  * the Initial Developer. All Rights Reserved.
26  *
27  * Contributor(s):
28  *
29  * Alternatively, the contents of this file may be used under the terms of
30  * either of the GNU General Public License Version 2 or later (the "GPL"),
31  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32  * in which case the provisions of the GPL or the LGPL are applicable instead
33  * of those above. If you wish to allow use of your version of this file only
34  * under the terms of either the GPL or the LGPL, and not to allow others to
35  * use your version of this file under the terms of the MPL, indicate your
36  * decision by deleting the provisions above and replace them with the notice
37  * and other provisions required by the GPL or the LGPL. If you do not delete
38  * the provisions above, a recipient may use your version of this file under
39  * the terms of any one of the MPL, the GPL or the LGPL.
40  *
41  */
42
43 #pragma once
44
45 #include <math.h>
46 #include <stdint.h>
47 #include <string.h>
48 #include <time.h>
49 #include <wtf/WallTime.h>
50 #include <wtf/text/WTFString.h>
51
52 namespace WTF {
53
54 enum TimeType {
55     UTCTime = 0,
56     LocalTime
57 };
58
59 struct LocalTimeOffset {
60     WTF_MAKE_STRUCT_FAST_ALLOCATED;
61
62     LocalTimeOffset()
63         : isDST(false)
64         , offset(0)
65     {
66     }
67
68     LocalTimeOffset(bool isDST, int offset)
69         : isDST(isDST)
70         , offset(offset)
71     {
72     }
73
74     bool operator==(const LocalTimeOffset& other)
75     {
76         return isDST == other.isDST && offset == other.offset;
77     }
78
79     bool operator!=(const LocalTimeOffset& other)
80     {
81         return isDST != other.isDST || offset != other.offset;
82     }
83
84     bool isDST;
85     int offset;
86 };
87
88 void initializeDates();
89 int equivalentYearForDST(int year);
90
91 // Not really math related, but this is currently the only shared place to put these.
92 WTF_EXPORT_PRIVATE double parseES5DateFromNullTerminatedCharacters(const char* dateString, bool& isLocalTime);
93 WTF_EXPORT_PRIVATE double parseDateFromNullTerminatedCharacters(const char* dateString);
94 WTF_EXPORT_PRIVATE double parseDateFromNullTerminatedCharacters(const char* dateString, bool& isLocalTime);
95 // dayOfWeek: [0, 6] 0 being Monday, day: [1, 31], month: [0, 11], year: ex: 2011, hours: [0, 23], minutes: [0, 59], seconds: [0, 59], utcOffset: [-720,720]. 
96 String makeRFC2822DateString(unsigned dayOfWeek, unsigned day, unsigned month, unsigned year, unsigned hours, unsigned minutes, unsigned seconds, int utcOffset);
97
98 inline double jsCurrentTime()
99 {
100     // JavaScript doesn't recognize fractions of a millisecond.
101     return floor(WallTime::now().secondsSinceEpoch().milliseconds());
102 }
103
104 extern WTF_EXPORT_PRIVATE const char* const weekdayName[7];
105 extern WTF_EXPORT_PRIVATE const char* const monthName[12];
106 extern WTF_EXPORT_PRIVATE const char* const monthFullName[12];
107 extern WTF_EXPORT_PRIVATE const int firstDayOfMonth[2][12];
108
109 static constexpr double hoursPerDay = 24.0;
110 static constexpr double minutesPerHour = 60.0;
111 static constexpr double secondsPerMinute = 60.0;
112 static constexpr double msPerSecond = 1000.0;
113 static constexpr double msPerMonth = 2592000000.0;
114 static constexpr double secondsPerHour = secondsPerMinute * minutesPerHour;
115 static constexpr double secondsPerDay = secondsPerHour * hoursPerDay;
116 static constexpr double msPerMinute = msPerSecond * secondsPerMinute;
117 static constexpr double msPerHour = msPerSecond * secondsPerHour;
118 static constexpr double msPerDay = msPerSecond * secondsPerDay;
119
120 static constexpr double maxUnixTime = 2145859200.0; // 12/31/2037
121 // ECMAScript asks not to support for a date of which total
122 // millisecond value is larger than the following value.
123 // See 15.9.1.14 of ECMA-262 5th edition.
124 static constexpr double maxECMAScriptTime = 8.64E15;
125
126 class TimeClippedPositiveMilliseconds {
127 public:
128     static constexpr int64_t hoursPerDay = 24;
129     static constexpr int64_t minutesPerHour = 60;
130     static constexpr int64_t secondsPerMinute = 60;
131     static constexpr int64_t msPerSecond = 1000;
132     static constexpr int64_t msPerMonth = 2592000000;
133     static constexpr int64_t secondsPerHour = secondsPerMinute * minutesPerHour;
134     static constexpr int64_t secondsPerDay = secondsPerHour * hoursPerDay;
135     static constexpr int64_t msPerMinute = msPerSecond * secondsPerMinute;
136     static constexpr int64_t msPerHour = msPerSecond * secondsPerHour;
137     static constexpr int64_t msPerDay = msPerSecond * secondsPerDay;
138     static constexpr int64_t maxECMAScriptTime = 8.64E15;
139
140     explicit TimeClippedPositiveMilliseconds(int64_t value)
141         : m_value(value)
142     {
143         ASSERT(value >= 0);
144     }
145
146     int64_t value() const { return m_value; }
147     double asDouble() const { return static_cast<double>(m_value); }
148 private:
149     int64_t m_value;
150 };
151
152 inline double timeClip(double t)
153 {
154     if (std::abs(t) > maxECMAScriptTime)
155         return std::numeric_limits<double>::quiet_NaN();
156     return std::trunc(t) + 0.0;
157 }
158
159 inline double daysFrom1970ToYear(int year)
160 {
161     // The Gregorian Calendar rules for leap years:
162     // Every fourth year is a leap year. 2004, 2008, and 2012 are leap years.
163     // However, every hundredth year is not a leap year. 1900 and 2100 are not leap years.
164     // Every four hundred years, there's a leap year after all. 2000 and 2400 are leap years.
165
166     static constexpr int leapDaysBefore1971By4Rule = 1970 / 4;
167     static constexpr int excludedLeapDaysBefore1971By100Rule = 1970 / 100;
168     static constexpr int leapDaysBefore1971By400Rule = 1970 / 400;
169
170     const double yearMinusOne = year - 1;
171     const double yearsToAddBy4Rule = floor(yearMinusOne / 4.0) - leapDaysBefore1971By4Rule;
172     const double yearsToExcludeBy100Rule = floor(yearMinusOne / 100.0) - excludedLeapDaysBefore1971By100Rule;
173     const double yearsToAddBy400Rule = floor(yearMinusOne / 400.0) - leapDaysBefore1971By400Rule;
174
175     return 365.0 * (year - 1970.0) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule;
176 }
177
178 inline int64_t daysFrom1970ToYearTimeClippedPositive(int year)
179 {
180     static constexpr int leapDaysBefore1971By4Rule = 1970 / 4;
181     static constexpr int excludedLeapDaysBefore1971By100Rule = 1970 / 100;
182     static constexpr int leapDaysBefore1971By400Rule = 1970 / 400;
183
184     ASSERT(year >= 1970);
185     const int64_t yearMinusOne = year - 1;
186     const int64_t yearsToAddBy4Rule = yearMinusOne / 4.0 - leapDaysBefore1971By4Rule;
187     const int64_t yearsToExcludeBy100Rule = yearMinusOne / 100.0 - excludedLeapDaysBefore1971By100Rule;
188     const int64_t yearsToAddBy400Rule = yearMinusOne / 400.0 - leapDaysBefore1971By400Rule;
189
190     return 365 * (year - 1970) + yearsToAddBy4Rule - yearsToExcludeBy100Rule + yearsToAddBy400Rule;
191 }
192
193 inline bool isLeapYear(int year)
194 {
195     if (year % 4 != 0)
196         return false;
197     if (year % 400 == 0)
198         return true;
199     if (year % 100 == 0)
200         return false;
201     return true;
202 }
203
204 inline int daysInYear(int year)
205 {
206     return 365 + isLeapYear(year);
207 }
208
209 inline double msToDays(double ms)
210 {
211     return floor(ms / msPerDay);
212 }
213
214 inline int64_t msToDays(TimeClippedPositiveMilliseconds ms)
215 {
216     return ms.value() / TimeClippedPositiveMilliseconds::msPerDay;
217 }
218
219 inline int dayInYear(int year, int month, int day)
220 {
221     return firstDayOfMonth[isLeapYear(year)][month] + day - 1;
222 }
223
224 inline int dayInYear(double ms, int year)
225 {
226     return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year));
227 }
228
229 inline int dayInYear(TimeClippedPositiveMilliseconds ms, int year)
230 {
231     return static_cast<int>(msToDays(ms) - daysFrom1970ToYearTimeClippedPositive(year));
232 }
233
234 // Returns the number of days from 1970-01-01 to the specified date.
235 inline double dateToDaysFrom1970(int year, int month, int day)
236 {
237     year += month / 12;
238
239     month %= 12;
240     if (month < 0) {
241         month += 12;
242         --year;
243     }
244
245     double yearday = floor(daysFrom1970ToYear(year));
246     ASSERT((year >= 1970 && yearday >= 0) || (year < 1970 && yearday < 0));
247     return yearday + dayInYear(year, month, day);
248 }
249
250 inline int msToYear(double ms)
251 {
252     int approxYear = static_cast<int>(floor(ms / (msPerDay * 365.2425)) + 1970);
253     double msFromApproxYearTo1970 = msPerDay * daysFrom1970ToYear(approxYear);
254     if (msFromApproxYearTo1970 > ms)
255         return approxYear - 1;
256     if (msFromApproxYearTo1970 + msPerDay * daysInYear(approxYear) <= ms)
257         return approxYear + 1;
258     return approxYear;
259 }
260
261 inline int msToMinutes(double ms)
262 {
263     double result = fmod(floor(ms / msPerMinute), minutesPerHour);
264     if (result < 0)
265         result += minutesPerHour;
266     return static_cast<int>(result);
267 }
268
269 inline int msToMinutes(TimeClippedPositiveMilliseconds ms)
270 {
271     int64_t result = (ms.value() / TimeClippedPositiveMilliseconds::msPerMinute) % TimeClippedPositiveMilliseconds::minutesPerHour;
272     ASSERT(result >= 0);
273     return static_cast<int>(result);
274 }
275
276 inline int msToHours(double ms)
277 {
278     double result = fmod(floor(ms / msPerHour), hoursPerDay);
279     if (result < 0)
280         result += hoursPerDay;
281     return static_cast<int>(result);
282 }
283
284 inline int msToHours(TimeClippedPositiveMilliseconds ms)
285 {
286     int64_t result = (ms.value() / TimeClippedPositiveMilliseconds::msPerHour) % TimeClippedPositiveMilliseconds::hoursPerDay;
287     ASSERT(result >= 0);
288     return static_cast<int>(result);
289 }
290
291 inline int msToSeconds(double ms)
292 {
293     double result = fmod(floor(ms / msPerSecond), secondsPerMinute);
294     if (result < 0)
295         result += secondsPerMinute;
296     return static_cast<int>(result);
297 }
298
299 inline int msToSeconds(TimeClippedPositiveMilliseconds ms)
300 {
301     int64_t result = ms.value() / TimeClippedPositiveMilliseconds::msPerSecond % TimeClippedPositiveMilliseconds::secondsPerMinute;
302     ASSERT(result >= 0);
303     return static_cast<int>(result);
304 }
305
306 // 0: Sunday, 1: Monday, etc.
307 inline int msToWeekDay(double ms)
308 {
309     int wd = (static_cast<int>(msToDays(ms)) + 4) % 7;
310     if (wd < 0)
311         wd += 7;
312     return wd;
313 }
314
315 inline int msToWeekDay(TimeClippedPositiveMilliseconds ms)
316 {
317     int result = (static_cast<int>(msToDays(ms)) + 4) % 7;
318     ASSERT(result >= 0);
319     return result;
320 }
321
322 inline int monthFromDayInYear(int dayInYear, bool leapYear)
323 {
324     const int d = dayInYear;
325     int step;
326
327     if (d < (step = 31))
328         return 0;
329     step += (leapYear ? 29 : 28);
330     if (d < step)
331         return 1;
332     if (d < (step += 31))
333         return 2;
334     if (d < (step += 30))
335         return 3;
336     if (d < (step += 31))
337         return 4;
338     if (d < (step += 30))
339         return 5;
340     if (d < (step += 31))
341         return 6;
342     if (d < (step += 31))
343         return 7;
344     if (d < (step += 30))
345         return 8;
346     if (d < (step += 31))
347         return 9;
348     if (d < step + 30)
349         return 10;
350     return 11;
351 }
352
353 inline int dayInMonthFromDayInYear(int dayInYear, bool leapYear)
354 {
355     auto checkMonth = [] (int dayInYear, int& startDayOfThisMonth, int& startDayOfNextMonth, int daysInThisMonth) -> bool {
356         startDayOfThisMonth = startDayOfNextMonth;
357         startDayOfNextMonth += daysInThisMonth;
358         return (dayInYear <= startDayOfNextMonth);
359     };
360
361     const int d = dayInYear;
362     int step;
363     int next = 30;
364
365     if (d <= next)
366         return d + 1;
367     const int daysInFeb = (leapYear ? 29 : 28);
368     if (checkMonth(d, step, next, daysInFeb))
369         return d - step;
370     if (checkMonth(d, step, next, 31))
371         return d - step;
372     if (checkMonth(d, step, next, 30))
373         return d - step;
374     if (checkMonth(d, step, next, 31))
375         return d - step;
376     if (checkMonth(d, step, next, 30))
377         return d - step;
378     if (checkMonth(d, step, next, 31))
379         return d - step;
380     if (checkMonth(d, step, next, 31))
381         return d - step;
382     if (checkMonth(d, step, next, 30))
383         return d - step;
384     if (checkMonth(d, step, next, 31))
385         return d - step;
386     if (checkMonth(d, step, next, 30))
387         return d - step;
388     step = next;
389     return d - step;
390 }
391
392 // Returns combined offset in millisecond (UTC + DST).
393 WTF_EXPORT_PRIVATE LocalTimeOffset calculateLocalTimeOffset(double utcInMilliseconds, TimeType = UTCTime);
394
395 } // namespace WTF
396
397 using WTF::isLeapYear;
398 using WTF::dateToDaysFrom1970;
399 using WTF::dayInMonthFromDayInYear;
400 using WTF::dayInYear;
401 using WTF::minutesPerHour;
402 using WTF::monthFromDayInYear;
403 using WTF::msPerDay;
404 using WTF::msPerHour;
405 using WTF::msPerMinute;
406 using WTF::msPerSecond;
407 using WTF::msToYear;
408 using WTF::msToDays;
409 using WTF::msToMinutes;
410 using WTF::msToHours;
411 using WTF::secondsPerDay;
412 using WTF::secondsPerMinute;
413 using WTF::parseDateFromNullTerminatedCharacters;
414 using WTF::makeRFC2822DateString;
415 using WTF::LocalTimeOffset;
416 using WTF::calculateLocalTimeOffset;
417 using WTF::timeClip;
418 using WTF::jsCurrentTime;