/[jscoverage]/trunk/js/prmjtime.cpp
ViewVC logotype

Contents of /trunk/js/prmjtime.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (show annotations)
Sun Jan 10 07:23:34 2010 UTC (9 years, 7 months ago) by siliconforks
File size: 28444 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
18 *
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
39
40 /*
41 * PR time code.
42 */
43 #ifdef SOLARIS
44 #define _REENTRANT 1
45 #endif
46 #include <string.h>
47 #include <time.h>
48 #include "jstypes.h"
49 #include "jsstdint.h"
50 #include "jsutil.h"
51
52 #include "jsprf.h"
53 #include "jslock.h"
54 #include "prmjtime.h"
55
56 #define PRMJ_DO_MILLISECONDS 1
57
58 #ifdef XP_OS2
59 #include <sys/timeb.h>
60 #endif
61 #ifdef XP_WIN
62 #include <windef.h>
63 #include <winbase.h>
64 #include <math.h> /* for fabs */
65 #include <mmsystem.h> /* for timeBegin/EndPeriod */
66 /* VC++ 8.0 or later, and not WINCE */
67 #if _MSC_VER >= 1400 && !defined(WINCE)
68 #define NS_HAVE_INVALID_PARAMETER_HANDLER 1
69 #endif
70 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
71 #include <stdlib.h> /* for _set_invalid_parameter_handler */
72 #include <crtdbg.h> /* for _CrtSetReportMode */
73 #endif
74
75 #ifdef JS_THREADSAFE
76 #include <prinit.h>
77 #endif
78
79 #endif
80
81 #if defined(XP_UNIX) || defined(XP_BEOS)
82
83 #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
84 extern int gettimeofday(struct timeval *tv);
85 #endif
86
87 #include <sys/time.h>
88
89 #endif /* XP_UNIX */
90
91 #define PRMJ_YEAR_DAYS 365L
92 #define PRMJ_FOUR_YEARS_DAYS (4 * PRMJ_YEAR_DAYS + 1)
93 #define PRMJ_CENTURY_DAYS (25 * PRMJ_FOUR_YEARS_DAYS - 1)
94 #define PRMJ_FOUR_CENTURIES_DAYS (4 * PRMJ_CENTURY_DAYS + 1)
95 #define PRMJ_HOUR_SECONDS 3600L
96 #define PRMJ_DAY_SECONDS (24L * PRMJ_HOUR_SECONDS)
97 #define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * PRMJ_YEAR_DAYS)
98 #define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */
99
100 /* function prototypes */
101 static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm);
102 /*
103 * get the difference in seconds between this time zone and UTC (GMT)
104 */
105 JSInt32
106 PRMJ_LocalGMTDifference()
107 {
108 struct tm ltime;
109
110 #if defined(XP_WIN) && !defined(WINCE)
111 /* Windows does not follow POSIX. Updates to the
112 * TZ environment variable are not reflected
113 * immediately on that platform as they are
114 * on UNIX systems without this call.
115 */
116 _tzset();
117 #endif
118 /* get the difference between this time zone and GMT */
119 memset((char *)&ltime,0,sizeof(ltime));
120 ltime.tm_mday = 2;
121 ltime.tm_year = 70;
122 return (JSInt32)mktime(&ltime) - (24L * 3600L);
123 }
124
125 /* Constants for GMT offset from 1970 */
126 #define G1970GMTMICROHI 0x00dcdcad /* micro secs to 1970 hi */
127 #define G1970GMTMICROLOW 0x8b3fa000 /* micro secs to 1970 low */
128
129 #define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */
130 #define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */
131
132 /* Convert from base time to extended time */
133 static JSInt64
134 PRMJ_ToExtendedTime(JSInt32 base_time)
135 {
136 JSInt64 exttime;
137 JSInt64 g1970GMTMicroSeconds;
138 JSInt64 low;
139 JSInt32 diff;
140 JSInt64 tmp;
141 JSInt64 tmp1;
142
143 diff = PRMJ_LocalGMTDifference();
144 JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC);
145 JSLL_I2L(tmp1,diff);
146 JSLL_MUL(tmp,tmp,tmp1);
147
148 JSLL_UI2L(g1970GMTMicroSeconds,G1970GMTMICROHI);
149 JSLL_UI2L(low,G1970GMTMICROLOW);
150 JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16);
151 JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16);
152 JSLL_ADD(g1970GMTMicroSeconds,g1970GMTMicroSeconds,low);
153
154 JSLL_I2L(exttime,base_time);
155 JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds);
156 JSLL_SUB(exttime,exttime,tmp);
157 return exttime;
158 }
159
160 #ifdef HAVE_SYSTEMTIMETOFILETIME
161
162 static const JSInt64 win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000);
163
164 #define FILETIME2INT64(ft) (((JSInt64)ft.dwHighDateTime) << 32LL | (JSInt64)ft.dwLowDateTime)
165
166 #endif
167
168 #if defined(HAVE_GETSYSTEMTIMEASFILETIME) || defined(HAVE_SYSTEMTIMETOFILETIME)
169
170 #if defined(HAVE_GETSYSTEMTIMEASFILETIME)
171 inline void
172 LowResTime(LPFILETIME lpft)
173 {
174 GetSystemTimeAsFileTime(lpft);
175 }
176 #elif defined(HAVE_SYSTEMTIMETOFILETIME)
177 inline void
178 LowResTime(LPFILETIME lpft)
179 {
180 GetCurrentFT(lpft);
181 }
182 #else
183 #error "No implementation of PRMJ_Now was selected."
184 #endif
185
186 typedef struct CalibrationData {
187 long double freq; /* The performance counter frequency */
188 long double offset; /* The low res 'epoch' */
189 long double timer_offset; /* The high res 'epoch' */
190
191 /* The last high res time that we returned since recalibrating */
192 JSInt64 last;
193
194 JSBool calibrated;
195
196 #ifdef JS_THREADSAFE
197 CRITICAL_SECTION data_lock;
198 CRITICAL_SECTION calibration_lock;
199 #endif
200 #ifdef WINCE
201 JSInt64 granularity;
202 #endif
203 } CalibrationData;
204
205 static CalibrationData calibration = { 0 };
206
207 static void
208 NowCalibrate()
209 {
210 FILETIME ft, ftStart;
211 LARGE_INTEGER liFreq, now;
212
213 if (calibration.freq == 0.0) {
214 if(!QueryPerformanceFrequency(&liFreq)) {
215 /* High-performance timer is unavailable */
216 calibration.freq = -1.0;
217 } else {
218 calibration.freq = (long double) liFreq.QuadPart;
219 }
220 }
221 if (calibration.freq > 0.0) {
222 JSInt64 calibrationDelta = 0;
223
224 /* By wrapping a timeBegin/EndPeriod pair of calls around this loop,
225 the loop seems to take much less time (1 ms vs 15ms) on Vista. */
226 timeBeginPeriod(1);
227 LowResTime(&ftStart);
228 do {
229 LowResTime(&ft);
230 } while (memcmp(&ftStart,&ft, sizeof(ft)) == 0);
231 timeEndPeriod(1);
232
233 #ifdef WINCE
234 calibration.granularity = (FILETIME2INT64(ft) -
235 FILETIME2INT64(ftStart))/10;
236 #endif
237 /*
238 calibrationDelta = (FILETIME2INT64(ft) - FILETIME2INT64(ftStart))/10;
239 fprintf(stderr, "Calibration delta was %I64d us\n", calibrationDelta);
240 */
241
242 QueryPerformanceCounter(&now);
243
244 calibration.offset = (long double) FILETIME2INT64(ft);
245 calibration.timer_offset = (long double) now.QuadPart;
246
247 /* The windows epoch is around 1600. The unix epoch is around
248 1970. win2un is the difference (in windows time units which
249 are 10 times more highres than the JS time unit) */
250 calibration.offset -= win2un;
251 calibration.offset *= 0.1;
252 calibration.last = 0;
253
254 calibration.calibrated = JS_TRUE;
255 }
256 }
257
258 #define CALIBRATIONLOCK_SPINCOUNT 0
259 #define DATALOCK_SPINCOUNT 4096
260 #define LASTLOCK_SPINCOUNT 4096
261
262 #ifdef JS_THREADSAFE
263 static PRStatus
264 NowInit(void)
265 {
266 memset(&calibration, 0, sizeof(calibration));
267 NowCalibrate();
268 #ifdef WINCE
269 InitializeCriticalSection(&calibration.calibration_lock);
270 InitializeCriticalSection(&calibration.data_lock);
271 #else
272 InitializeCriticalSectionAndSpinCount(&calibration.calibration_lock, CALIBRATIONLOCK_SPINCOUNT);
273 InitializeCriticalSectionAndSpinCount(&calibration.data_lock, DATALOCK_SPINCOUNT);
274 #endif
275 return PR_SUCCESS;
276 }
277
278 void
279 PRMJ_NowShutdown()
280 {
281 DeleteCriticalSection(&calibration.calibration_lock);
282 DeleteCriticalSection(&calibration.data_lock);
283 }
284
285 #define MUTEX_LOCK(m) EnterCriticalSection(m)
286 #define MUTEX_TRYLOCK(m) TryEnterCriticalSection(m)
287 #define MUTEX_UNLOCK(m) LeaveCriticalSection(m)
288 #ifdef WINCE
289 #define MUTEX_SETSPINCOUNT(m, c)
290 #else
291 #define MUTEX_SETSPINCOUNT(m, c) SetCriticalSectionSpinCount((m),(c))
292 #endif
293
294 static PRCallOnceType calibrationOnce = { 0 };
295
296 #else
297
298 #define MUTEX_LOCK(m)
299 #define MUTEX_TRYLOCK(m) 1
300 #define MUTEX_UNLOCK(m)
301 #define MUTEX_SETSPINCOUNT(m, c)
302
303 #endif
304
305 #endif /* HAVE_GETSYSTEMTIMEASFILETIME */
306
307
308 #if defined(XP_OS2)
309 JSInt64
310 PRMJ_Now(void)
311 {
312 JSInt64 s, us, ms2us, s2us;
313 struct timeb b;
314
315 ftime(&b);
316 JSLL_UI2L(ms2us, PRMJ_USEC_PER_MSEC);
317 JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC);
318 JSLL_UI2L(s, b.time);
319 JSLL_UI2L(us, b.millitm);
320 JSLL_MUL(us, us, ms2us);
321 JSLL_MUL(s, s, s2us);
322 JSLL_ADD(s, s, us);
323 return s;
324 }
325
326 #elif defined(XP_UNIX) || defined(XP_BEOS)
327 JSInt64
328 PRMJ_Now(void)
329 {
330 struct timeval tv;
331 JSInt64 s, us, s2us;
332
333 #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
334 gettimeofday(&tv);
335 #else
336 gettimeofday(&tv, 0);
337 #endif /* _SVID_GETTOD */
338 JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC);
339 JSLL_UI2L(s, tv.tv_sec);
340 JSLL_UI2L(us, tv.tv_usec);
341 JSLL_MUL(s, s, s2us);
342 JSLL_ADD(s, s, us);
343 return s;
344 }
345
346 #else
347 /*
348
349 Win32 python-esque pseudo code
350 Please see bug 363258 for why the win32 timing code is so complex.
351
352 calibration mutex : Win32CriticalSection(spincount=0)
353 data mutex : Win32CriticalSection(spincount=4096)
354
355 def NowInit():
356 init mutexes
357 PRMJ_NowCalibration()
358
359 def NowCalibration():
360 expensive up-to-15ms call
361
362 def PRMJ_Now():
363 returnedTime = 0
364 needCalibration = False
365 cachedOffset = 0.0
366 calibrated = False
367 PR_CallOnce(PRMJ_NowInit)
368 do
369 if not global.calibrated or needCalibration:
370 acquire calibration mutex
371 acquire data mutex
372
373 // Only recalibrate if someone didn't already
374 if cachedOffset == calibration.offset:
375 // Have all waiting threads immediately wait
376 set data mutex spin count = 0
377 PRMJ_NowCalibrate()
378 calibrated = 1
379
380 set data mutex spin count = default
381 release data mutex
382 release calibration mutex
383
384 calculate lowres time
385
386 if highres timer available:
387 acquire data mutex
388 calculate highres time
389 cachedOffset = calibration.offset
390 highres time = calibration.last = max(highres time, calibration.last)
391 release data mutex
392
393 get kernel tick interval
394
395 if abs(highres - lowres) < kernel tick:
396 returnedTime = highres time
397 needCalibration = False
398 else:
399 if calibrated:
400 returnedTime = lowres
401 needCalibration = False
402 else:
403 needCalibration = True
404 else:
405 returnedTime = lowres
406 while needCalibration
407
408 */
409
410 JSInt64
411 PRMJ_Now(void)
412 {
413 static int nCalls = 0;
414 long double lowresTime, highresTimerValue;
415 FILETIME ft;
416 LARGE_INTEGER now;
417 JSBool calibrated = JS_FALSE;
418 JSBool needsCalibration = JS_FALSE;
419 JSInt64 returnedTime;
420 long double cachedOffset = 0.0;
421
422 /* To avoid regressing startup time (where high resolution is likely
423 not needed), give the old behavior for the first few calls.
424 This does not appear to be needed on Vista as the timeBegin/timeEndPeriod
425 calls seem to immediately take effect. */
426 int thiscall = JS_ATOMIC_INCREMENT(&nCalls);
427 /* 10 seems to be the number of calls to load with a blank homepage */
428 if (thiscall <= 10) {
429 LowResTime(&ft);
430 return (FILETIME2INT64(ft)-win2un)/10L;
431 }
432
433 /* For non threadsafe platforms, NowInit is not necessary */
434 #ifdef JS_THREADSAFE
435 PR_CallOnce(&calibrationOnce, NowInit);
436 #endif
437 do {
438 if (!calibration.calibrated || needsCalibration) {
439 MUTEX_LOCK(&calibration.calibration_lock);
440 MUTEX_LOCK(&calibration.data_lock);
441
442 /* Recalibrate only if no one else did before us */
443 if(calibration.offset == cachedOffset) {
444 /* Since calibration can take a while, make any other
445 threads immediately wait */
446 MUTEX_SETSPINCOUNT(&calibration.data_lock, 0);
447
448 NowCalibrate();
449
450 calibrated = JS_TRUE;
451
452 /* Restore spin count */
453 MUTEX_SETSPINCOUNT(&calibration.data_lock, DATALOCK_SPINCOUNT);
454 }
455 MUTEX_UNLOCK(&calibration.data_lock);
456 MUTEX_UNLOCK(&calibration.calibration_lock);
457 }
458
459
460 /* Calculate a low resolution time */
461 LowResTime(&ft);
462 lowresTime = 0.1*(long double)(FILETIME2INT64(ft) - win2un);
463
464 if (calibration.freq > 0.0) {
465 long double highresTime, diff;
466
467 DWORD timeAdjustment, timeIncrement;
468 BOOL timeAdjustmentDisabled;
469
470 /* Default to 15.625 ms if the syscall fails */
471 long double skewThreshold = 15625.25;
472 /* Grab high resolution time */
473 QueryPerformanceCounter(&now);
474 highresTimerValue = (long double)now.QuadPart;
475
476 MUTEX_LOCK(&calibration.data_lock);
477 highresTime = calibration.offset + PRMJ_USEC_PER_SEC*
478 (highresTimerValue-calibration.timer_offset)/calibration.freq;
479 cachedOffset = calibration.offset;
480
481 /* On some dual processor/core systems, we might get an earlier time
482 so we cache the last time that we returned */
483 calibration.last = JS_MAX(calibration.last,(JSInt64)highresTime);
484 returnedTime = calibration.last;
485 MUTEX_UNLOCK(&calibration.data_lock);
486
487 #ifdef WINCE
488 /* Get an estimate of clock ticks per second from our own test */
489 skewThreshold = calibration.granularity;
490 #else
491 /* Rather than assume the NT kernel ticks every 15.6ms, ask it */
492 if (GetSystemTimeAdjustment(&timeAdjustment,
493 &timeIncrement,
494 &timeAdjustmentDisabled)) {
495 if (timeAdjustmentDisabled) {
496 /* timeAdjustment is in units of 100ns */
497 skewThreshold = timeAdjustment/10.0;
498 } else {
499 /* timeIncrement is in units of 100ns */
500 skewThreshold = timeIncrement/10.0;
501 }
502 }
503 #endif
504 /* Check for clock skew */
505 diff = lowresTime - highresTime;
506
507 /* For some reason that I have not determined, the skew can be
508 up to twice a kernel tick. This does not seem to happen by
509 itself, but I have only seen it triggered by another program
510 doing some kind of file I/O. The symptoms are a negative diff
511 followed by an equally large positive diff. */
512 if (fabs(diff) > 2*skewThreshold) {
513 /*fprintf(stderr,"Clock skew detected (diff = %f)!\n", diff);*/
514
515 if (calibrated) {
516 /* If we already calibrated once this instance, and the
517 clock is still skewed, then either the processor(s) are
518 wildly changing clockspeed or the system is so busy that
519 we get switched out for long periods of time. In either
520 case, it would be infeasible to make use of high
521 resolution results for anything, so let's resort to old
522 behavior for this call. It's possible that in the
523 future, the user will want the high resolution timer, so
524 we don't disable it entirely. */
525 returnedTime = (JSInt64)lowresTime;
526 needsCalibration = JS_FALSE;
527 } else {
528 /* It is possible that when we recalibrate, we will return a
529 value less than what we have returned before; this is
530 unavoidable. We cannot tell the different between a
531 faulty QueryPerformanceCounter implementation and user
532 changes to the operating system time. Since we must
533 respect user changes to the operating system time, we
534 cannot maintain the invariant that Date.now() never
535 decreases; the old implementation has this behavior as
536 well. */
537 needsCalibration = JS_TRUE;
538 }
539 } else {
540 /* No detectable clock skew */
541 returnedTime = (JSInt64)highresTime;
542 needsCalibration = JS_FALSE;
543 }
544 } else {
545 /* No high resolution timer is available, so fall back */
546 returnedTime = (JSInt64)lowresTime;
547 }
548 } while (needsCalibration);
549
550 return returnedTime;
551 }
552 #endif
553
554 /* Get the DST timezone offset for the time passed in */
555 JSInt64
556 PRMJ_DSTOffset(JSInt64 local_time)
557 {
558 JSInt64 us2s;
559 time_t local;
560 JSInt32 diff;
561 JSInt64 maxtimet;
562 struct tm tm;
563 PRMJTime prtm;
564 #ifndef HAVE_LOCALTIME_R
565 struct tm *ptm;
566 #endif
567
568
569 JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC);
570 JSLL_DIV(local_time, local_time, us2s);
571
572 /* get the maximum of time_t value */
573 JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET);
574
575 if(JSLL_CMP(local_time,>,maxtimet)){
576 JSLL_UI2L(local_time,PRMJ_MAX_UNIX_TIMET);
577 } else if(!JSLL_GE_ZERO(local_time)){
578 /*go ahead a day to make localtime work (does not work with 0) */
579 JSLL_UI2L(local_time,PRMJ_DAY_SECONDS);
580 }
581
582 #if defined(XP_WIN) && !defined(WINCE)
583 /* Windows does not follow POSIX. Updates to the
584 * TZ environment variable are not reflected
585 * immediately on that platform as they are
586 * on UNIX systems without this call.
587 */
588 _tzset();
589 #endif
590
591 JSLL_L2UI(local,local_time);
592 PRMJ_basetime(local_time,&prtm);
593 #ifndef HAVE_LOCALTIME_R
594 ptm = localtime(&local);
595 if(!ptm){
596 return 0;
597 }
598 tm = *ptm;
599 #else
600 localtime_r(&local,&tm); /* get dst information */
601 #endif
602
603 diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) +
604 ((tm.tm_min - prtm.tm_min) * 60);
605
606 if (diff < 0)
607 diff += PRMJ_DAY_SECONDS;
608
609 JSLL_UI2L(local_time,diff);
610
611 JSLL_MUL(local_time,local_time,us2s);
612
613 return(local_time);
614 }
615
616 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
617 static void
618 PRMJ_InvalidParameterHandler(const wchar_t *expression,
619 const wchar_t *function,
620 const wchar_t *file,
621 unsigned int line,
622 uintptr_t pReserved)
623 {
624 /* empty */
625 }
626 #endif
627
628 /* Format a time value into a buffer. Same semantics as strftime() */
629 size_t
630 PRMJ_FormatTime(char *buf, int buflen, const char *fmt, PRMJTime *prtm)
631 {
632 size_t result = 0;
633 #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
634 struct tm a;
635 int fake_tm_year = 0;
636 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
637 _invalid_parameter_handler oldHandler;
638 int oldReportMode;
639 #endif
640
641 /* Zero out the tm struct. Linux, SunOS 4 struct tm has extra members int
642 * tm_gmtoff, char *tm_zone; when tm_zone is garbage, strftime gets
643 * confused and dumps core. NSPR20 prtime.c attempts to fill these in by
644 * calling mktime on the partially filled struct, but this doesn't seem to
645 * work as well; the result string has "can't get timezone" for ECMA-valid
646 * years. Might still make sense to use this, but find the range of years
647 * for which valid tz information exists, and map (per ECMA hint) from the
648 * given year into that range.
649
650 * N.B. This hasn't been tested with anything that actually _uses_
651 * tm_gmtoff; zero might be the wrong thing to set it to if you really need
652 * to format a time. This fix is for jsdate.c, which only uses
653 * JS_FormatTime to get a string representing the time zone. */
654 memset(&a, 0, sizeof(struct tm));
655
656 a.tm_sec = prtm->tm_sec;
657 a.tm_min = prtm->tm_min;
658 a.tm_hour = prtm->tm_hour;
659 a.tm_mday = prtm->tm_mday;
660 a.tm_mon = prtm->tm_mon;
661 a.tm_wday = prtm->tm_wday;
662
663 #if defined(HAVE_LOCALTIME_R) && defined(HAVE_TM_ZONE_TM_GMTOFF)
664 {
665 struct tm td;
666 time_t bogus = 0;
667 localtime_r(&bogus, &td);
668 a.tm_gmtoff = td.tm_gmtoff;
669 a.tm_zone = td.tm_zone;
670 }
671 #endif
672
673 /*
674 * Years before 1900 and after 9999 cause strftime() to abort on Windows.
675 * To avoid that we replace it with FAKE_YEAR_BASE + year % 100 and then
676 * replace matching substrings in the strftime() result with the real year.
677 * Note that FAKE_YEAR_BASE should be a multiple of 100 to make 2-digit
678 * year formats (%y) work correctly (since we won't find the fake year
679 * in that case).
680 * e.g. new Date(1873, 0).toLocaleFormat('%Y %y') => "1873 73"
681 * See bug 327869.
682 */
683 #define FAKE_YEAR_BASE 9900
684 if (prtm->tm_year < 1900 || prtm->tm_year > 9999) {
685 fake_tm_year = FAKE_YEAR_BASE + prtm->tm_year % 100;
686 a.tm_year = fake_tm_year - 1900;
687 }
688 else {
689 a.tm_year = prtm->tm_year - 1900;
690 }
691 a.tm_yday = prtm->tm_yday;
692 a.tm_isdst = prtm->tm_isdst;
693
694 /*
695 * Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff
696 * are null. This doesn't quite work, though - the timezone is off by
697 * tzoff + dst. (And mktime seems to return -1 for the exact dst
698 * changeover time.)
699 */
700
701 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
702 oldHandler = _set_invalid_parameter_handler(PRMJ_InvalidParameterHandler);
703 oldReportMode = _CrtSetReportMode(_CRT_ASSERT, 0);
704 #endif
705
706 result = strftime(buf, buflen, fmt, &a);
707
708 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
709 _set_invalid_parameter_handler(oldHandler);
710 _CrtSetReportMode(_CRT_ASSERT, oldReportMode);
711 #endif
712
713 if (fake_tm_year && result) {
714 char real_year[16];
715 char fake_year[16];
716 size_t real_year_len;
717 size_t fake_year_len;
718 char* p;
719
720 sprintf(real_year, "%d", prtm->tm_year);
721 real_year_len = strlen(real_year);
722 sprintf(fake_year, "%d", fake_tm_year);
723 fake_year_len = strlen(fake_year);
724
725 /* Replace the fake year in the result with the real year. */
726 for (p = buf; (p = strstr(p, fake_year)); p += real_year_len) {
727 size_t new_result = result + real_year_len - fake_year_len;
728 if ((int)new_result >= buflen) {
729 return 0;
730 }
731 memmove(p + real_year_len, p + fake_year_len, strlen(p + fake_year_len));
732 memcpy(p, real_year, real_year_len);
733 result = new_result;
734 *(buf + result) = '\0';
735 }
736 }
737 #endif
738 return result;
739 }
740
741 /* table for number of days in a month */
742 static int mtab[] = {
743 /* jan, feb,mar,apr,may,jun */
744 31,28,31,30,31,30,
745 /* july,aug,sep,oct,nov,dec */
746 31,31,30,31,30,31
747 };
748
749 /*
750 * basic time calculation functionality for localtime and gmtime
751 * setups up prtm argument with correct values based upon input number
752 * of seconds.
753 */
754 static void
755 PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm)
756 {
757 /* convert tsecs back to year,month,day,hour,secs */
758 JSInt32 year = 0;
759 JSInt32 month = 0;
760 JSInt32 yday = 0;
761 JSInt32 mday = 0;
762 JSInt32 wday = 6; /* start on a Sunday */
763 JSInt32 days = 0;
764 JSInt32 seconds = 0;
765 JSInt32 minutes = 0;
766 JSInt32 hours = 0;
767 JSInt32 isleap = 0;
768
769 /* Temporaries used for various computations */
770 JSInt64 result;
771 JSInt64 result1;
772 JSInt64 result2;
773
774 JSInt64 base;
775
776 /* Some variables for intermediate result storage to make computing isleap
777 easier/faster */
778 JSInt32 fourCenturyBlocks;
779 JSInt32 centuriesLeft;
780 JSInt32 fourYearBlocksLeft;
781 JSInt32 yearsLeft;
782
783 /* Since leap years work by 400/100/4 year intervals, precompute the length
784 of those in seconds if they start at the beginning of year 1. */
785 JSInt64 fourYears;
786 JSInt64 century;
787 JSInt64 fourCenturies;
788
789 JSLL_UI2L(result, PRMJ_DAY_SECONDS);
790
791 JSLL_I2L(fourYears, PRMJ_FOUR_YEARS_DAYS);
792 JSLL_MUL(fourYears, fourYears, result);
793
794 JSLL_I2L(century, PRMJ_CENTURY_DAYS);
795 JSLL_MUL(century, century, result);
796
797 JSLL_I2L(fourCenturies, PRMJ_FOUR_CENTURIES_DAYS);
798 JSLL_MUL(fourCenturies, fourCenturies, result);
799
800 /* get the base time via UTC */
801 base = PRMJ_ToExtendedTime(0);
802 JSLL_UI2L(result, PRMJ_USEC_PER_SEC);
803 JSLL_DIV(base,base,result);
804 JSLL_ADD(tsecs,tsecs,base);
805
806 /* Compute our |year|, |isleap|, and part of |days|. When this part is
807 done, |year| should hold the year our date falls in (number of whole
808 years elapsed before our date), isleap should hold 1 if the year the
809 date falls in is a leap year and 0 otherwise. */
810
811 /* First do year 0; it's special and nonleap. */
812 JSLL_UI2L(result, PRMJ_YEAR_SECONDS);
813 if (!JSLL_CMP(tsecs,<,result)) {
814 days = PRMJ_YEAR_DAYS;
815 year = 1;
816 JSLL_SUB(tsecs, tsecs, result);
817 }
818
819 /* Now use those constants we computed above */
820 JSLL_UDIVMOD(&result1, &result2, tsecs, fourCenturies);
821 JSLL_L2I(fourCenturyBlocks, result1);
822 year += fourCenturyBlocks * 400;
823 days += fourCenturyBlocks * PRMJ_FOUR_CENTURIES_DAYS;
824 tsecs = result2;
825
826 JSLL_UDIVMOD(&result1, &result2, tsecs, century);
827 JSLL_L2I(centuriesLeft, result1);
828 year += centuriesLeft * 100;
829 days += centuriesLeft * PRMJ_CENTURY_DAYS;
830 tsecs = result2;
831
832 JSLL_UDIVMOD(&result1, &result2, tsecs, fourYears);
833 JSLL_L2I(fourYearBlocksLeft, result1);
834 year += fourYearBlocksLeft * 4;
835 days += fourYearBlocksLeft * PRMJ_FOUR_YEARS_DAYS;
836 tsecs = result2;
837
838 /* Recall that |result| holds PRMJ_YEAR_SECONDS */
839 JSLL_UDIVMOD(&result1, &result2, tsecs, result);
840 JSLL_L2I(yearsLeft, result1);
841 year += yearsLeft;
842 days += yearsLeft * PRMJ_YEAR_DAYS;
843 tsecs = result2;
844
845 /* now compute isleap. Note that we don't have to use %, since we've
846 already computed those remainders. Also note that they're all offset by
847 1 because of the 1 for year 0. */
848 isleap =
849 (yearsLeft == 3) && (fourYearBlocksLeft != 24 || centuriesLeft == 3);
850 JS_ASSERT(isleap ==
851 ((year % 4 == 0) && (year % 100 != 0 || year % 400 == 0)));
852
853 JSLL_UI2L(result1,PRMJ_DAY_SECONDS);
854
855 JSLL_DIV(result,tsecs,result1);
856 JSLL_L2I(mday,result);
857
858 /* let's find the month */
859 while(((month == 1 && isleap) ?
860 (mday >= mtab[month] + 1) :
861 (mday >= mtab[month]))){
862 yday += mtab[month];
863 days += mtab[month];
864
865 mday -= mtab[month];
866
867 /* it's a Feb, check if this is a leap year */
868 if(month == 1 && isleap != 0){
869 yday++;
870 days++;
871 mday--;
872 }
873 month++;
874 }
875
876 /* now adjust tsecs */
877 JSLL_MUL(result,result,result1);
878 JSLL_SUB(tsecs,tsecs,result);
879
880 mday++; /* day of month always start with 1 */
881 days += mday;
882 wday = (days + wday) % 7;
883
884 yday += mday;
885
886 /* get the hours */
887 JSLL_UI2L(result1,PRMJ_HOUR_SECONDS);
888 JSLL_DIV(result,tsecs,result1);
889 JSLL_L2I(hours,result);
890 JSLL_MUL(result,result,result1);
891 JSLL_SUB(tsecs,tsecs,result);
892
893 /* get minutes */
894 JSLL_UI2L(result1,60);
895 JSLL_DIV(result,tsecs,result1);
896 JSLL_L2I(minutes,result);
897 JSLL_MUL(result,result,result1);
898 JSLL_SUB(tsecs,tsecs,result);
899
900 JSLL_L2I(seconds,tsecs);
901
902 prtm->tm_usec = 0L;
903 prtm->tm_sec = (JSInt8)seconds;
904 prtm->tm_min = (JSInt8)minutes;
905 prtm->tm_hour = (JSInt8)hours;
906 prtm->tm_mday = (JSInt8)mday;
907 prtm->tm_mon = (JSInt8)month;
908 prtm->tm_wday = (JSInt8)wday;
909 prtm->tm_year = (JSInt16)year;
910 prtm->tm_yday = (JSInt16)yday;
911 }

  ViewVC Help
Powered by ViewVC 1.1.24