1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2006, 2007 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
52namespace WTF {
53
54enum TimeType {
55 UTCTime = 0,
56 LocalTime
57};
58
59struct 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
88void initializeDates();
89int equivalentYearForDST(int year);
90
91// Not really math related, but this is currently the only shared place to put these.
92WTF_EXPORT_PRIVATE double parseES5DateFromNullTerminatedCharacters(const char* dateString);
93WTF_EXPORT_PRIVATE double parseDateFromNullTerminatedCharacters(const char* dateString);
94WTF_EXPORT_PRIVATE double parseDateFromNullTerminatedCharacters(const char* dateString, bool& haveTZ, int& offset);
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].
96String makeRFC2822DateString(unsigned dayOfWeek, unsigned day, unsigned month, unsigned year, unsigned hours, unsigned minutes, unsigned seconds, int utcOffset);
97
98inline double jsCurrentTime()
99{
100 // JavaScript doesn't recognize fractions of a millisecond.
101 return floor(WallTime::now().secondsSinceEpoch().milliseconds());
102}
103
104extern WTF_EXPORT_PRIVATE const char* const weekdayName[7];
105extern WTF_EXPORT_PRIVATE const char* const monthName[12];
106extern WTF_EXPORT_PRIVATE const char* const monthFullName[12];
107extern WTF_EXPORT_PRIVATE const int firstDayOfMonth[2][12];
108
109static constexpr double hoursPerDay = 24.0;
110static constexpr double minutesPerHour = 60.0;
111static constexpr double secondsPerMinute = 60.0;
112static constexpr double msPerSecond = 1000.0;
113static constexpr double msPerMonth = 2592000000.0;
114static constexpr double secondsPerHour = secondsPerMinute * minutesPerHour;
115static constexpr double secondsPerDay = secondsPerHour * hoursPerDay;
116static constexpr double msPerMinute = msPerSecond * secondsPerMinute;
117static constexpr double msPerHour = msPerSecond * secondsPerHour;
118static constexpr double msPerDay = msPerSecond * secondsPerDay;
119
120static 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.
124static constexpr double maxECMAScriptTime = 8.64E15;
125
126class TimeClippedPositiveMilliseconds {
127public:
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); }
148private:
149 int64_t m_value;
150};
151
152inline 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
159inline 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
178inline 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
193inline 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
204inline int daysInYear(int year)
205{
206 return 365 + isLeapYear(year);
207}
208
209inline double msToDays(double ms)
210{
211 return floor(ms / msPerDay);
212}
213
214inline int64_t msToDays(TimeClippedPositiveMilliseconds ms)
215{
216 return ms.value() / TimeClippedPositiveMilliseconds::msPerDay;
217}
218
219inline int dayInYear(int year, int month, int day)
220{
221 return firstDayOfMonth[isLeapYear(year)][month] + day - 1;
222}
223
224inline int dayInYear(double ms, int year)
225{
226 return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year));
227}
228
229inline 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.
235inline 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
250inline 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
261inline 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
269inline 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
276inline 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
284inline 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
291inline 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
299inline 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.
307inline 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
315inline int msToWeekDay(TimeClippedPositiveMilliseconds ms)
316{
317 int result = (static_cast<int>(msToDays(ms)) + 4) % 7;
318 ASSERT(result >= 0);
319 return result;
320}
321
322inline 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
353inline 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).
393WTF_EXPORT_PRIVATE LocalTimeOffset calculateLocalTimeOffset(double utcInMilliseconds, TimeType = UTCTime);
394
395} // namespace WTF
396
397using WTF::isLeapYear;
398using WTF::dateToDaysFrom1970;
399using WTF::dayInMonthFromDayInYear;
400using WTF::dayInYear;
401using WTF::minutesPerHour;
402using WTF::monthFromDayInYear;
403using WTF::msPerDay;
404using WTF::msPerHour;
405using WTF::msPerMinute;
406using WTF::msPerSecond;
407using WTF::msToYear;
408using WTF::msToDays;
409using WTF::msToMinutes;
410using WTF::msToHours;
411using WTF::secondsPerDay;
412using WTF::secondsPerMinute;
413using WTF::parseDateFromNullTerminatedCharacters;
414using WTF::makeRFC2822DateString;
415using WTF::LocalTimeOffset;
416using WTF::calculateLocalTimeOffset;
417using WTF::timeClip;
418using WTF::jsCurrentTime;
419