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

Contents of /trunk/js/prmjtime.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 399 - (show annotations)
Tue Dec 9 03:37:47 2008 UTC (10 years, 11 months ago) by siliconforks
File size: 26927 byte(s)
Use SpiderMonkey from Firefox 3.1b2.

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

  ViewVC Help
Powered by ViewVC 1.1.24