source: webkit/trunk/JavaScriptCore/kjs/DateMath.cpp@ 19268

Last change on this file since 19268 was 19268, checked in by darin, 18 years ago
  • kjs/DateMath.cpp: Fix license header to reflect LGPL as the first license mentioned. We still mention the option of using under MPL or GPL since some of this code came from the Mozilla project with those license terms.
File size: 11.2 KB
Line 
1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
4 *
5 * The Original Code is Mozilla Communicator client code, released
6 * March 31, 1998.
7 *
8 * The Initial Developer of the Original Code is
9 * Netscape Communications Corporation.
10 * Portions created by the Initial Developer are Copyright (C) 1998
11 * the Initial Developer. All Rights Reserved.
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 *
27 * Alternatively, the contents of this file may be used under the terms
28 * of either the Mozilla Public License Version 1.1, found at
29 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
30 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
31 * (the "GPL"), in which case the provisions of the MPL or the GPL are
32 * applicable instead of those above. If you wish to allow use of your
33 * version of this file only under the terms of one of those two
34 * licenses (the MPL or the GPL) and not to allow others to use your
35 * version of this file under the LGPL, indicate your decision by
36 * deletingthe provisions above and replace them with the notice and
37 * other provisions required by the MPL or the GPL, as the case may be.
38 * If you do not delete the provisions above, a recipient may use your
39 * version of this file under any of the LGPL, the MPL or the GPL.
40 */
41
42#include "config.h"
43#include "DateMath.h"
44
45#include <math.h>
46#include <stdint.h>
47#include <wtf/OwnPtr.h>
48
49namespace KJS {
50
51/* Constants */
52
53static const double minutesPerDay = 24.0 * 60.0;
54static const double secondsPerDay = 24.0 * 60.0 * 60.0;
55static const double secondsPerYear = 24.0 * 60.0 * 60.0 * 365.0;
56
57static const double usecPerSec = 1000000.0;
58
59static const double maxUnixTime = 2145859200.0; /*equivalent to 12/31/2037 */
60
61/*
62 * The following array contains the day of year for the first day of
63 * each month, where index 0 is January, and day 0 is January 1.
64 */
65static int firstDayOfMonth[2][12] = {
66 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
67 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
68};
69
70static inline int daysInYear(int year)
71{
72 if (year % 4 != 0)
73 return 365;
74 if (year % 400 == 0)
75 return 366;
76 if (year % 100 == 0)
77 return 365;
78 return 366;
79}
80
81static inline double daysFrom1970ToYear(int year)
82{
83 return 365.0 * (year - 1970)
84 + floor((year - 1969) / 4.0)
85 - floor((year - 1901) / 100.0)
86 + floor((year - 1601) / 400.0);
87}
88
89static inline double msFrom1970ToYear(int year)
90{
91 return msPerDay * daysFrom1970ToYear(year);
92}
93
94static inline double msToDays(double ms)
95{
96 return floor(ms / msPerDay);
97}
98
99static inline int msToYear(double ms)
100{
101 int y = static_cast<int>(floor(ms /(msPerDay*365.2425)) + 1970);
102 double t2 = msFrom1970ToYear(y);
103
104 if (t2 > ms) {
105 y--;
106 } else {
107 if (t2 + msPerDay * daysInYear(y) <= ms)
108 y++;
109 }
110 return y;
111}
112
113static inline bool isLeapYear(int year)
114{
115 if (year % 4 != 0)
116 return false;
117 if (year % 400 == 0)
118 return true;
119 if (year % 100 == 0)
120 return false;
121 return true;
122}
123
124static inline bool isInLeapYear(double ms)
125{
126 return isLeapYear(msToYear(ms));
127}
128
129static inline int dayInYear(double ms, int year)
130{
131 return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year));
132}
133
134static inline double msToMilliseconds(double ms)
135{
136 double result;
137 result = fmod(ms, msPerDay);
138 if (result < 0)
139 result += msPerDay;
140 return result;
141}
142
143// 0: Sunday, 1: Monday, etc.
144static inline int msToWeekDay(double ms)
145{
146 int wd = ((int)msToDays(ms) + 4) % 7;
147 if (wd < 0)
148 wd += 7;
149 return wd;
150}
151
152static inline int msToSeconds(double ms)
153{
154 int result = (int) fmod(floor(ms / msPerSecond), secondsPerMinute);
155 if (result < 0)
156 result += (int)secondsPerMinute;
157 return result;
158}
159
160static inline int msToMinutes(double ms)
161{
162 int result = (int) fmod(floor(ms / msPerMinute), minutesPerHour);
163 if (result < 0)
164 result += (int)minutesPerHour;
165 return result;
166}
167
168static inline int msToHours(double ms)
169{
170 int result = (int) fmod(floor(ms/msPerHour), hoursPerDay);
171 if (result < 0)
172 result += (int)hoursPerDay;
173 return result;
174}
175
176static inline int msToMonth(double ms)
177{
178 int d, step;
179 int year = msToYear(ms);
180 d = dayInYear(ms, year);
181
182 if (d < (step = 31))
183 return 0;
184 step += (isInLeapYear(ms) ? 29 : 28);
185 if (d < step)
186 return 1;
187 if (d < (step += 31))
188 return 2;
189 if (d < (step += 30))
190 return 3;
191 if (d < (step += 31))
192 return 4;
193 if (d < (step += 30))
194 return 5;
195 if (d < (step += 31))
196 return 6;
197 if (d < (step += 31))
198 return 7;
199 if (d < (step += 30))
200 return 8;
201 if (d < (step += 31))
202 return 9;
203 if (d < (step += 30))
204 return 10;
205 return 11;
206}
207
208static inline int msToDayInMonth(double ms)
209{
210 int d, step, next;
211 int year = msToYear(ms);
212 d = dayInYear(ms, year);
213
214 if (d <= (next = 30))
215 return d + 1;
216 step = next;
217 next += (isInLeapYear(ms) ? 29 : 28);
218 if (d <= next)
219 return d - step;
220 step = next;
221 if (d <= (next += 31))
222 return d - step;
223 step = next;
224 if (d <= (next += 30))
225 return d - step;
226 step = next;
227 if (d <= (next += 31))
228 return d - step;
229 step = next;
230 if (d <= (next += 30))
231 return d - step;
232 step = next;
233 if (d <= (next += 31))
234 return d - step;
235 step = next;
236 if (d <= (next += 31))
237 return d - step;
238 step = next;
239 if (d <= (next += 30))
240 return d - step;
241 step = next;
242 if (d <= (next += 31))
243 return d - step;
244 step = next;
245 if (d <= (next += 30))
246 return d - step;
247 step = next;
248 return d - step;
249}
250
251static inline int monthToDayInYear(int month, bool isLeapYear)
252{
253 return firstDayOfMonth[isLeapYear][month];
254}
255
256static inline double timeToMS(double hour, double min, double sec, double ms)
257{
258 return (((hour * minutesPerHour + min) * secondsPerMinute + sec) * msPerSecond + ms);
259}
260
261static int dateToDayInYear(int year, int month, int day)
262{
263 year += month / 12;
264
265 month %= 12;
266 if (month < 0) {
267 month += 12;
268 --year;
269 }
270
271 int yearday = static_cast<int>(floor(msFrom1970ToYear(year) / msPerDay));
272 int monthday = monthToDayInYear(month, isLeapYear(year));
273
274 return yearday + monthday + day - 1;
275}
276
277/*
278 * Find a year for which any given date will fall on the same weekday.
279 *
280 * This function should be used with caution when used other than
281 * for determining DST; it hasn't been proven not to produce an
282 * incorrect year for times near year boundaries.
283 */
284int equivalentYearForDST(int year)
285{
286 int difference = 2000 - year; // Arbitrary year around which most dates equivalence is correct
287 int quotient = difference / 28; // Integer division, no remainder.
288 int product = quotient * 28;
289 return year + product;
290}
291
292/*
293 * Get the difference in milliseconds between this time zone and UTC (GMT)
294 * NOT including DST.
295 */
296double getUTCOffset() {
297 tm localt;
298
299 memset(&localt, 0, sizeof(localt));
300
301 // get the difference between this time zone and UTC on Jan 01, 2000 12:00:00 AM
302 localt.tm_mday = 1;
303 localt.tm_year = 100;
304 double utcOffset = 946684800.0 - mktime(&localt);
305
306 utcOffset *= msPerSecond;
307
308 return utcOffset;
309}
310
311/*
312 * Get the DST offset for the time passed in. Takes
313 * seconds (not milliseconds) and cannot handle dates before 1970
314 * on some OS'
315 */
316static double getDSTOffsetSimple(double localTimeSeconds)
317{
318 if(localTimeSeconds > maxUnixTime)
319 localTimeSeconds = maxUnixTime;
320 else if(localTimeSeconds < 0) // Go ahead a day to make localtime work (does not work with 0)
321 localTimeSeconds += secondsPerDay;
322
323 //input is UTC so we have to shift back to local time to determine DST thus the + getUTCOffset()
324 double offsetTime = (localTimeSeconds * msPerSecond) + getUTCOffset() ;
325
326 // Offset from UTC but doesn't include DST obviously
327 int offsetHour = msToHours(offsetTime);
328 int offsetMinute = msToMinutes(offsetTime);
329
330 // FIXME: time_t has a potential problem in 2038
331 time_t localTime = static_cast<time_t>(localTimeSeconds);
332
333 tm localTM;
334 #if PLATFORM(WIN_OS)
335 localtime_s(&localTM, &localTime);
336 #else
337 localtime_r(&localTime, &localTM);
338 #endif
339
340 double diff = ((localTM.tm_hour - offsetHour) * secondsPerHour) + ((localTM.tm_min - offsetMinute) * 60);
341
342 if(diff < 0)
343 diff += secondsPerDay;
344
345 return (diff * msPerSecond);
346}
347
348// Get the DST offset the time passed in
349// ms is in UTC
350static double getDSTOffset(double ms)
351{
352 // On mac the call to localtime (see getDSTOffsetSimple) will return historically accurate
353 // DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript
354 // standard explicitly dictates that historical information should not be considered when
355 // determining DST. For this reason we shift years that localtime can handle but would
356 // return historically accurate information.
357
358 // if before Jan 01, 2000 12:00:00 AM UTC or after Jan 01, 2038 12:00:00 AM UTC
359 if (ms < 946684800000.0 || ms > 2145916800000.0) {
360 int year;
361 int day;
362
363 year = equivalentYearForDST(msToYear(ms));
364 day = dateToDayInYear(year, msToMonth(ms), msToDayInMonth(ms));
365 ms = (day * msPerDay) + msToMilliseconds(ms);
366 }
367
368 return getDSTOffsetSimple(ms / msPerSecond);
369}
370
371double gregorianDateTimeToMS(const GregorianDateTime& t, double milliSeconds, bool inputIsUTC)
372{
373
374 int day = dateToDayInYear(t.year + 1900, t.month, t.monthDay);
375 double ms = timeToMS(t.hour, t.minute, t.second, milliSeconds);
376 double result = (day * msPerDay) + ms;
377
378 if(!inputIsUTC) { // convert to UTC
379 result -= getUTCOffset();
380 result -= getDSTOffset(result);
381 }
382
383 return result;
384}
385
386void msToGregorianDateTime(double ms, bool outputIsUTC, struct GregorianDateTime& tm)
387{
388 // input is UTC
389 double dstOff = 0.0;
390
391 if(!outputIsUTC) { // convert to local time
392 dstOff = getDSTOffset(ms);
393 ms += dstOff + getUTCOffset();
394 }
395
396 tm.second = msToSeconds(ms);
397 tm.minute = msToMinutes(ms);
398 tm.hour = msToHours(ms);
399 tm.weekDay = msToWeekDay(ms);
400 tm.monthDay = msToDayInMonth(ms);
401 tm.yearDay = dayInYear(ms, msToYear(ms));
402 tm.month = msToMonth(ms);
403 tm.year = msToYear(ms) - 1900;
404 tm.isDST = dstOff != 0.0;
405
406 tm.utcOffset = static_cast<long>((dstOff + getUTCOffset()) / msPerSecond);
407 tm.timeZone = NULL;
408}
409
410} // namespace KJS
Note: See TracBrowser for help on using the repository browser.