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

Annotation of /trunk/js/prmjtime.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

1 siliconforks 507 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 siliconforks 332 *
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 siliconforks 507 #include "jsstdint.h"
50 siliconforks 332 #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 siliconforks 460 #if defined(XP_WIN) && !defined(WINCE)
111     /* Windows does not follow POSIX. Updates to the
112 siliconforks 507 * TZ environment variable are not reflected
113 siliconforks 460 * immediately on that platform as they are
114     * on UNIX systems without this call.
115     */
116     _tzset();
117     #endif
118 siliconforks 332 /* 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 siliconforks 507 #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 siliconforks 332 {
174 siliconforks 507 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 siliconforks 332 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 siliconforks 507 #ifdef WINCE
201     JSInt64 granularity;
202     #endif
203 siliconforks 332 } 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 siliconforks 507 LowResTime(&ftStart);
228 siliconforks 332 do {
229 siliconforks 507 LowResTime(&ft);
230 siliconforks 332 } while (memcmp(&ftStart,&ft, sizeof(ft)) == 0);
231     timeEndPeriod(1);
232    
233 siliconforks 507 #ifdef WINCE
234     calibration.granularity = (FILETIME2INT64(ft) -
235     FILETIME2INT64(ftStart))/10;
236     #endif
237 siliconforks 332 /*
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 siliconforks 399 static PRStatus
264 siliconforks 332 NowInit(void)
265     {
266     memset(&calibration, 0, sizeof(calibration));
267     NowCalibrate();
268 siliconforks 507 #ifdef WINCE
269     InitializeCriticalSection(&calibration.calibration_lock);
270     InitializeCriticalSection(&calibration.data_lock);
271     #else
272 siliconforks 332 InitializeCriticalSectionAndSpinCount(&calibration.calibration_lock, CALIBRATIONLOCK_SPINCOUNT);
273     InitializeCriticalSectionAndSpinCount(&calibration.data_lock, DATALOCK_SPINCOUNT);
274 siliconforks 507 #endif
275 siliconforks 332 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 siliconforks 507 #ifdef WINCE
289     #define MUTEX_SETSPINCOUNT(m, c)
290     #else
291 siliconforks 332 #define MUTEX_SETSPINCOUNT(m, c) SetCriticalSectionSpinCount((m),(c))
292 siliconforks 507 #endif
293 siliconforks 332
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 siliconforks 507 #endif /* HAVE_GETSYSTEMTIMEASFILETIME */
306 siliconforks 332
307    
308 siliconforks 507 #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 siliconforks 332 /*
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 siliconforks 507 LowResTime(&ft);
430 siliconforks 332 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 siliconforks 507 LowResTime(&ft);
462 siliconforks 332 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 siliconforks 507 calibration.last = JS_MAX(calibration.last,(JSInt64)highresTime);
484 siliconforks 332 returnedTime = calibration.last;
485     MUTEX_UNLOCK(&calibration.data_lock);
486    
487 siliconforks 507 #ifdef WINCE
488     /* Get an estimate of clock ticks per second from our own test */
489     skewThreshold = calibration.granularity;
490     #else
491 siliconforks 332 /* 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 siliconforks 507 #endif
504 siliconforks 332 /* 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 siliconforks 507 }
552 siliconforks 332 #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 siliconforks 460
582     #if defined(XP_WIN) && !defined(WINCE)
583     /* Windows does not follow POSIX. Updates to the
584 siliconforks 507 * TZ environment variable are not reflected
585 siliconforks 460 * immediately on that platform as they are
586     * on UNIX systems without this call.
587     */
588     _tzset();
589     #endif
590    
591 siliconforks 332 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 siliconforks 460 ((tm.tm_min - prtm.tm_min) * 60);
605 siliconforks 332
606 siliconforks 460 if (diff < 0)
607     diff += PRMJ_DAY_SECONDS;
608 siliconforks 332
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 siliconforks 460 #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 siliconforks 332 /*
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