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

Last change on this file since 16855 was 16855, checked in by kmccullo, 19 years ago

Reviewed by Brady.

DST and TimeZones were wrong in some cases, specifically on some of the dates where DST changes.

  • kjs/DateMath.cpp: (KJS::equivalentYearForDST): (KJS::getUTCOffset): (KJS::getDSTOffsetSimple): (KJS::getDSTOffset): (KJS::dateToMseconds): (KJS::msToTM):
  • kjs/DateMath.h:
  • kjs/date_object.cpp: (KJS::gmtoffset):
File size: 11.4 KB
Line 
1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2006 Apple Computer
4 *
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
19 *
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
24 *
25 * Contributor(s):
26 *
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
38 *
39 */
40
41#include <DateMath.h>
42
43#include <math.h>
44#include <stdint.h>
45#include <wtf/OwnPtr.h>
46
47namespace KJS {
48
49/* Constants */
50
51static const double secondsPerHour = 60.0 * 60.0;
52static const double minutesPerDay = 24.0 * 60.0;
53static const double secondsPerDay = 24.0 * 60.0 * 60.0;
54static const double secondsPerYear = 24.0 * 60.0 * 60.0 * 365.0;
55
56
57static const double usecPerMsec = 1000.0;
58static const double usecPerSec = 1000000.0;
59
60static const double maxUnixTime = 2145859200.0; /*equivalent to 12/31/2037 */
61
62/*
63 * The following array contains the day of year for the first day of
64 * each month, where index 0 is January, and day 0 is January 1.
65 */
66static int firstDayOfMonth[2][12] = {
67 {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
68 {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
69};
70
71
72/*
73 * Years and leap years on which Jan 1 is a Sunday, Monday, etc.
74 *
75 * yearStartingWith[0][i] is an example non-leap year where
76 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
77 *
78 * yearStartingWith[1][i] is an example leap year where
79 * Jan 1 appears on Sunday (i == 0), Monday (i == 1), etc.
80 */
81static int yearStartingWith[2][7] = {
82 {1978, 1973, 1974, 1975, 1981, 1971, 1977},
83 {1984, 1996, 1980, 1992, 1976, 1988, 1972}
84};
85
86
87static inline int daysInYear(int year)
88{
89 if (year % 4 != 0)
90 return 365;
91 if (year % 400 == 0)
92 return 366;
93 if (year % 100 == 0)
94 return 365;
95 return 366;
96}
97
98static inline double daysFrom1970ToYear(int year)
99{
100 return 365.0 * (year - 1970)
101 + floor((year - 1969) / 4.0)
102 - floor((year - 1901) / 100.0)
103 + floor((year - 1601) / 400.0);
104}
105
106static inline double msFrom1970ToYear(int year)
107{
108 return msPerDay * daysFrom1970ToYear(year);
109}
110
111static inline double msToDays(double ms)
112{
113 return floor(ms / msPerDay);
114}
115
116static inline int msToYear(double ms)
117{
118 int y = static_cast<int>(floor(ms /(msPerDay*365.2425)) + 1970);
119 double t2 = msFrom1970ToYear(y);
120
121 if (t2 > ms) {
122 y--;
123 } else {
124 if (t2 + msPerDay * daysInYear(y) <= ms)
125 y++;
126 }
127 return y;
128}
129
130static inline bool isLeapYear(int year)
131{
132 if (year % 4 != 0)
133 return false;
134 if (year % 400 == 0)
135 return true;
136 if (year % 100 == 0)
137 return false;
138 return true;
139}
140
141static inline bool isInLeapYear(double ms)
142{
143 return isLeapYear(msToYear(ms));
144}
145
146static inline int dayInYear(double ms, int year)
147{
148 return static_cast<int>(msToDays(ms) - daysFrom1970ToYear(year));
149}
150
151static inline double msToMilliseconds(double ms)
152{
153 double result;
154 result = fmod(ms, msPerDay);
155 if (result < 0)
156 result += msPerDay;
157 return result;
158}
159
160// 0: Sunday, 1: Monday, etc.
161static inline int msToWeekDay(double ms)
162{
163 int wd = ((int)msToDays(ms) + 4) % 7;
164 if (wd < 0)
165 wd += 7;
166 return wd;
167}
168
169static inline int msToSeconds(double ms)
170{
171 int result = (int) fmod(floor(ms / msPerSecond), secondsPerMinute);
172 if (result < 0)
173 result += (int)secondsPerMinute;
174 return result;
175}
176
177static inline int msToMinutes(double ms)
178{
179 int result = (int) fmod(floor(ms / msPerMinute), minutesPerHour);
180 if (result < 0)
181 result += (int)minutesPerHour;
182 return result;
183}
184
185static inline int msToHours(double ms)
186{
187 int result = (int) fmod(floor(ms/msPerHour), hoursPerDay);
188 if (result < 0)
189 result += (int)hoursPerDay;
190 return result;
191}
192
193static inline int msToMonth(double ms)
194{
195 int d, step;
196 int year = msToYear(ms);
197 d = dayInYear(ms, year);
198
199 if (d < (step = 31))
200 return 0;
201 step += (isInLeapYear(ms) ? 29 : 28);
202 if (d < step)
203 return 1;
204 if (d < (step += 31))
205 return 2;
206 if (d < (step += 30))
207 return 3;
208 if (d < (step += 31))
209 return 4;
210 if (d < (step += 30))
211 return 5;
212 if (d < (step += 31))
213 return 6;
214 if (d < (step += 31))
215 return 7;
216 if (d < (step += 30))
217 return 8;
218 if (d < (step += 31))
219 return 9;
220 if (d < (step += 30))
221 return 10;
222 return 11;
223}
224
225static inline int msToDayInMonth(double ms)
226{
227 int d, step, next;
228 int year = msToYear(ms);
229 d = dayInYear(ms, year);
230
231 if (d <= (next = 30))
232 return d + 1;
233 step = next;
234 next += (isInLeapYear(ms) ? 29 : 28);
235 if (d <= next)
236 return d - step;
237 step = next;
238 if (d <= (next += 31))
239 return d - step;
240 step = next;
241 if (d <= (next += 30))
242 return d - step;
243 step = next;
244 if (d <= (next += 31))
245 return d - step;
246 step = next;
247 if (d <= (next += 30))
248 return d - step;
249 step = next;
250 if (d <= (next += 31))
251 return d - step;
252 step = next;
253 if (d <= (next += 31))
254 return d - step;
255 step = next;
256 if (d <= (next += 30))
257 return d - step;
258 step = next;
259 if (d <= (next += 31))
260 return d - step;
261 step = next;
262 if (d <= (next += 30))
263 return d - step;
264 step = next;
265 return d - step;
266}
267
268static inline int monthToDayInYear(int month, bool isLeapYear)
269{
270 return firstDayOfMonth[isLeapYear][month];
271}
272
273static inline double timeToMseconds(double hour, double min, double sec, double ms)
274{
275 return (((hour * minutesPerHour + min) * secondsPerMinute + sec) * msPerSecond + ms);
276}
277
278static int dateToDayInYear(int year, int month, int day)
279{
280 year += month / 12;
281
282 month %= 12;
283 if (month < 0) {
284 month += 12;
285 --year;
286 }
287
288 int yearday = static_cast<int>(floor(msFrom1970ToYear(year) / msPerDay));
289 int monthday = monthToDayInYear(month, isLeapYear(year));
290
291 return yearday + monthday + day - 1;
292}
293
294/*
295 * Find a year for which any given date will fall on the same weekday.
296 *
297 * This function should be used with caution when used other than
298 * for determining DST; it hasn't been proven not to produce an
299 * incorrect year for times near year boundaries.
300 */
301static inline int equivalentYearForDST(int year)
302{
303 int day;
304
305 day = (int) daysFrom1970ToYear(year) + 4;
306 day %= 7;
307
308 if (day < 0)
309 day += 7;
310
311 return yearStartingWith[isLeapYear(year)][day];
312}
313
314/*
315 * Get the difference in milliseconds between this time zone and UTC (GMT)
316 * NOT including DST.
317 */
318double getUTCOffset() {
319 static double utcOffset;
320 static bool utcOffsetInitialized = false;
321 if (!utcOffsetInitialized) {
322 struct tm ltime;
323
324 ltime.tm_sec = 0;
325 ltime.tm_min = 0;
326 ltime.tm_hour = 0;
327 ltime.tm_mon = 0;
328 ltime.tm_wday = 0;
329 ltime.tm_yday = 0;
330 ltime.tm_isdst = 0;
331
332 // get the difference between this time zone and GMT
333 ltime.tm_mday = 2;
334 ltime.tm_year = 70;
335
336#if !PLATFORM(WIN_OS)
337 ltime.tm_zone = 0;
338 ltime.tm_gmtoff = 0;
339#endif
340
341 utcOffset = mktime(&ltime) - (hoursPerDay * secondsPerHour);
342 utcOffset *= -msPerSecond;
343
344 utcOffsetInitialized = true;
345 }
346 return utcOffset;
347}
348
349/*
350 * Get the DST offset for the time passed in. Takes
351 * seconds (not milliseconds) and cannot handle dates before 1970
352 * on some OS'
353 */
354static double getDSTOffsetSimple(double localTimeSeconds)
355{
356 if(localTimeSeconds > maxUnixTime)
357 localTimeSeconds = maxUnixTime;
358 else if(localTimeSeconds < 0) // Go ahead a day to make localtime work (does not work with 0)
359 localTimeSeconds += secondsPerDay;
360
361 struct tm prtm;
362 double offsetTime = (localTimeSeconds * usecPerMsec) + getUTCOffset() ;
363
364 prtm.tm_hour = msToHours(offsetTime);
365 prtm.tm_min = msToMinutes(offsetTime);
366
367 // FIXME: time_t has a potential problem in 2038
368 time_t localTime = static_cast<time_t>(localTimeSeconds);
369
370 struct tm tm;
371 #if PLATFORM(WIN_OS)
372 localtime_s(&tm, &localTime);
373 #else
374 localtime_r(&localTime, &tm);
375 #endif
376
377 double diff = ((tm.tm_hour - prtm.tm_hour) * secondsPerHour) + ((tm.tm_min - prtm.tm_min) * 60);
378
379 if(diff < 0)
380 diff += secondsPerDay;
381
382 return (diff * usecPerMsec);
383}
384
385// get the DST offset the time passed in
386static double getDSTOffset(double ms)
387{
388 /*
389 * If earlier than 1970 or after 2038, potentially beyond the ken of
390 * many OSes, map it to an equivalent year before asking.
391 */
392 if (ms < 0.0 || ms > 2145916800000.0) {
393 int year;
394 int day;
395
396 year = equivalentYearForDST(msToYear(ms));
397 day = dateToDayInYear(year, msToMonth(ms), msToDayInMonth(ms));
398 ms = (day * msPerDay) + msToMilliseconds(ms);
399 }
400
401 return getDSTOffsetSimple(ms / usecPerMsec);
402}
403
404double dateToMseconds(tm* t, double ms, bool inputIsUTC)
405{
406 int day = dateToDayInYear(t->tm_year + 1900, t->tm_mon, t->tm_mday);
407 double msec_time = timeToMseconds(t->tm_hour, t->tm_min, t->tm_sec, ms);
408 double result = (day * msPerDay) + msec_time;
409
410 if(!inputIsUTC) { // convert to UTC
411 result -= getUTCOffset();
412 result -= getDSTOffset(result);
413 }
414
415 return result;
416}
417
418void msToTM(double ms, bool outputIsUTC, struct tm& tm)
419{
420 // input is UTC
421 double dstOff = 0.0;
422
423 if(!outputIsUTC) { // convert to local time
424 dstOff = getDSTOffset(ms);
425 ms += dstOff + getUTCOffset();
426 }
427
428 tm.tm_sec = msToSeconds(ms);
429 tm.tm_min = msToMinutes(ms);
430 tm.tm_hour = msToHours(ms);
431 tm.tm_wday = msToWeekDay(ms);
432 tm.tm_mday = msToDayInMonth(ms);
433 tm.tm_yday = dayInYear(ms, msToYear(ms));
434 tm.tm_mon = msToMonth(ms);
435 tm.tm_year = msToYear(ms) - 1900;
436 tm.tm_isdst = dstOff != 0.0;
437
438 // All other OS' seems to have these fields
439#if !PLATFORM(WIN_OS)
440 struct tm xtm;
441 // FIXME: time_t has a potential problem in 2038
442 time_t seconds = static_cast<time_t>(ms/usecPerMsec);
443 localtime_r(&seconds, &xtm);
444 tm.tm_gmtoff = xtm.tm_gmtoff;
445 tm.tm_zone = xtm.tm_zone;
446#endif
447}
448
449} // namespace KJS
450
Note: See TracBrowser for help on using the repository browser.