Bug 551763: Fix deletion of arguments ident. (r=Waldo)
[mozilla-central.git] / js / src / prmjtime.cpp
blobb9ee16161b6b5084258266a8e259b880a2e840fa
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
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.
24 * Contributor(s):
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.
38 * ***** END LICENSE BLOCK ***** */
41 * PR time code.
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"
52 #include "jsprf.h"
53 #include "jslock.h"
54 #include "prmjtime.h"
56 #define PRMJ_DO_MILLISECONDS 1
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
75 #ifdef JS_THREADSAFE
76 #include <prinit.h>
77 #endif
79 #endif
81 #if defined(XP_UNIX) || defined(XP_BEOS)
83 #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
84 extern int gettimeofday(struct timeval *tv);
85 #endif
87 #include <sys/time.h>
89 #endif /* XP_UNIX */
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 */
100 /* function prototypes */
101 static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm);
103 * get the difference in seconds between this time zone and UTC (GMT)
105 JSInt32
106 PRMJ_LocalGMTDifference()
108 struct tm ltime;
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.
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);
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 */
129 #define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */
130 #define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */
132 /* Convert from base time to extended time */
133 static JSInt64
134 PRMJ_ToExtendedTime(JSInt32 base_time)
136 JSInt64 exttime;
137 JSInt64 g1970GMTMicroSeconds;
138 JSInt64 low;
139 JSInt32 diff;
140 JSInt64 tmp;
141 JSInt64 tmp1;
143 diff = PRMJ_LocalGMTDifference();
144 JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC);
145 JSLL_I2L(tmp1,diff);
146 JSLL_MUL(tmp,tmp,tmp1);
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);
154 JSLL_I2L(exttime,base_time);
155 JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds);
156 JSLL_SUB(exttime,exttime,tmp);
157 return exttime;
160 #ifdef HAVE_SYSTEMTIMETOFILETIME
162 static const JSInt64 win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000);
164 #define FILETIME2INT64(ft) (((JSInt64)ft.dwHighDateTime) << 32LL | (JSInt64)ft.dwLowDateTime)
166 #endif
168 #if defined(HAVE_GETSYSTEMTIMEASFILETIME) || defined(HAVE_SYSTEMTIMETOFILETIME)
170 #if defined(HAVE_GETSYSTEMTIMEASFILETIME)
171 inline void
172 LowResTime(LPFILETIME lpft)
174 GetSystemTimeAsFileTime(lpft);
176 #elif defined(HAVE_SYSTEMTIMETOFILETIME)
177 inline void
178 LowResTime(LPFILETIME lpft)
180 GetCurrentFT(lpft);
182 #else
183 #error "No implementation of PRMJ_Now was selected."
184 #endif
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' */
191 /* The last high res time that we returned since recalibrating */
192 JSInt64 last;
194 JSBool calibrated;
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;
205 static CalibrationData calibration = { 0 };
207 static void
208 NowCalibrate()
210 FILETIME ft, ftStart;
211 LARGE_INTEGER liFreq, now;
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;
221 if (calibration.freq > 0.0) {
222 JSInt64 calibrationDelta = 0;
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);
233 #ifdef WINCE
234 calibration.granularity = (FILETIME2INT64(ft) -
235 FILETIME2INT64(ftStart))/10;
236 #endif
238 calibrationDelta = (FILETIME2INT64(ft) - FILETIME2INT64(ftStart))/10;
239 fprintf(stderr, "Calibration delta was %I64d us\n", calibrationDelta);
242 QueryPerformanceCounter(&now);
244 calibration.offset = (long double) FILETIME2INT64(ft);
245 calibration.timer_offset = (long double) now.QuadPart;
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;
254 calibration.calibrated = JS_TRUE;
258 #define CALIBRATIONLOCK_SPINCOUNT 0
259 #define DATALOCK_SPINCOUNT 4096
260 #define LASTLOCK_SPINCOUNT 4096
262 #ifdef JS_THREADSAFE
263 static PRStatus
264 NowInit(void)
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;
278 void
279 PRMJ_NowShutdown()
281 DeleteCriticalSection(&calibration.calibration_lock);
282 DeleteCriticalSection(&calibration.data_lock);
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
294 static PRCallOnceType calibrationOnce = { 0 };
296 #else
298 #define MUTEX_LOCK(m)
299 #define MUTEX_TRYLOCK(m) 1
300 #define MUTEX_UNLOCK(m)
301 #define MUTEX_SETSPINCOUNT(m, c)
303 #endif
305 #endif /* HAVE_GETSYSTEMTIMEASFILETIME */
308 #if defined(XP_OS2)
309 JSInt64
310 PRMJ_Now(void)
312 JSInt64 s, us, ms2us, s2us;
313 struct timeb b;
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;
326 #elif defined(XP_UNIX) || defined(XP_BEOS)
327 JSInt64
328 PRMJ_Now(void)
330 struct timeval tv;
331 JSInt64 s, us, s2us;
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;
346 #else
349 Win32 python-esque pseudo code
350 Please see bug 363258 for why the win32 timing code is so complex.
352 calibration mutex : Win32CriticalSection(spincount=0)
353 data mutex : Win32CriticalSection(spincount=4096)
355 def NowInit():
356 init mutexes
357 PRMJ_NowCalibration()
359 def NowCalibration():
360 expensive up-to-15ms call
362 def PRMJ_Now():
363 returnedTime = 0
364 needCalibration = False
365 cachedOffset = 0.0
366 calibrated = False
367 PR_CallOnce(PRMJ_NowInit)
369 if not global.calibrated or needCalibration:
370 acquire calibration mutex
371 acquire data mutex
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
380 set data mutex spin count = default
381 release data mutex
382 release calibration mutex
384 calculate lowres time
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
393 get kernel tick interval
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
410 // We parameterize the delay count just so that shell builds can
411 // set it to 0 in order to get high-resolution benchmarking.
412 // 10 seems to be the number of calls to load with a blank homepage.
413 int CALIBRATION_DELAY_COUNT = 10;
415 JSInt64
416 PRMJ_Now(void)
418 static int nCalls = 0;
419 long double lowresTime, highresTimerValue;
420 FILETIME ft;
421 LARGE_INTEGER now;
422 JSBool calibrated = JS_FALSE;
423 JSBool needsCalibration = JS_FALSE;
424 JSInt64 returnedTime;
425 long double cachedOffset = 0.0;
427 /* To avoid regressing startup time (where high resolution is likely
428 not needed), give the old behavior for the first few calls.
429 This does not appear to be needed on Vista as the timeBegin/timeEndPeriod
430 calls seem to immediately take effect. */
431 int thiscall = JS_ATOMIC_INCREMENT(&nCalls);
432 if (thiscall <= CALIBRATION_DELAY_COUNT) {
433 LowResTime(&ft);
434 return (FILETIME2INT64(ft)-win2un)/10L;
437 /* For non threadsafe platforms, NowInit is not necessary */
438 #ifdef JS_THREADSAFE
439 PR_CallOnce(&calibrationOnce, NowInit);
440 #endif
441 do {
442 if (!calibration.calibrated || needsCalibration) {
443 MUTEX_LOCK(&calibration.calibration_lock);
444 MUTEX_LOCK(&calibration.data_lock);
446 /* Recalibrate only if no one else did before us */
447 if(calibration.offset == cachedOffset) {
448 /* Since calibration can take a while, make any other
449 threads immediately wait */
450 MUTEX_SETSPINCOUNT(&calibration.data_lock, 0);
452 NowCalibrate();
454 calibrated = JS_TRUE;
456 /* Restore spin count */
457 MUTEX_SETSPINCOUNT(&calibration.data_lock, DATALOCK_SPINCOUNT);
459 MUTEX_UNLOCK(&calibration.data_lock);
460 MUTEX_UNLOCK(&calibration.calibration_lock);
464 /* Calculate a low resolution time */
465 LowResTime(&ft);
466 lowresTime = 0.1*(long double)(FILETIME2INT64(ft) - win2un);
468 if (calibration.freq > 0.0) {
469 long double highresTime, diff;
471 DWORD timeAdjustment, timeIncrement;
472 BOOL timeAdjustmentDisabled;
474 /* Default to 15.625 ms if the syscall fails */
475 long double skewThreshold = 15625.25;
476 /* Grab high resolution time */
477 QueryPerformanceCounter(&now);
478 highresTimerValue = (long double)now.QuadPart;
480 MUTEX_LOCK(&calibration.data_lock);
481 highresTime = calibration.offset + PRMJ_USEC_PER_SEC*
482 (highresTimerValue-calibration.timer_offset)/calibration.freq;
483 cachedOffset = calibration.offset;
485 /* On some dual processor/core systems, we might get an earlier time
486 so we cache the last time that we returned */
487 calibration.last = JS_MAX(calibration.last,(JSInt64)highresTime);
488 returnedTime = calibration.last;
489 MUTEX_UNLOCK(&calibration.data_lock);
491 #ifdef WINCE
492 /* Get an estimate of clock ticks per second from our own test */
493 skewThreshold = calibration.granularity;
494 #else
495 /* Rather than assume the NT kernel ticks every 15.6ms, ask it */
496 if (GetSystemTimeAdjustment(&timeAdjustment,
497 &timeIncrement,
498 &timeAdjustmentDisabled)) {
499 if (timeAdjustmentDisabled) {
500 /* timeAdjustment is in units of 100ns */
501 skewThreshold = timeAdjustment/10.0;
502 } else {
503 /* timeIncrement is in units of 100ns */
504 skewThreshold = timeIncrement/10.0;
507 #endif
508 /* Check for clock skew */
509 diff = lowresTime - highresTime;
511 /* For some reason that I have not determined, the skew can be
512 up to twice a kernel tick. This does not seem to happen by
513 itself, but I have only seen it triggered by another program
514 doing some kind of file I/O. The symptoms are a negative diff
515 followed by an equally large positive diff. */
516 if (fabs(diff) > 2*skewThreshold) {
517 /*fprintf(stderr,"Clock skew detected (diff = %f)!\n", diff);*/
519 if (calibrated) {
520 /* If we already calibrated once this instance, and the
521 clock is still skewed, then either the processor(s) are
522 wildly changing clockspeed or the system is so busy that
523 we get switched out for long periods of time. In either
524 case, it would be infeasible to make use of high
525 resolution results for anything, so let's resort to old
526 behavior for this call. It's possible that in the
527 future, the user will want the high resolution timer, so
528 we don't disable it entirely. */
529 returnedTime = (JSInt64)lowresTime;
530 needsCalibration = JS_FALSE;
531 } else {
532 /* It is possible that when we recalibrate, we will return a
533 value less than what we have returned before; this is
534 unavoidable. We cannot tell the different between a
535 faulty QueryPerformanceCounter implementation and user
536 changes to the operating system time. Since we must
537 respect user changes to the operating system time, we
538 cannot maintain the invariant that Date.now() never
539 decreases; the old implementation has this behavior as
540 well. */
541 needsCalibration = JS_TRUE;
543 } else {
544 /* No detectable clock skew */
545 returnedTime = (JSInt64)highresTime;
546 needsCalibration = JS_FALSE;
548 } else {
549 /* No high resolution timer is available, so fall back */
550 returnedTime = (JSInt64)lowresTime;
552 } while (needsCalibration);
554 return returnedTime;
556 #endif
558 /* Get the DST timezone offset for the time passed in */
559 JSInt64
560 PRMJ_DSTOffset(JSInt64 local_time)
562 JSInt64 us2s;
563 time_t local;
564 JSInt32 diff;
565 JSInt64 maxtimet;
566 struct tm tm;
567 PRMJTime prtm;
568 #ifndef HAVE_LOCALTIME_R
569 struct tm *ptm;
570 #endif
573 JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC);
574 JSLL_DIV(local_time, local_time, us2s);
576 /* get the maximum of time_t value */
577 JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET);
579 if(JSLL_CMP(local_time,>,maxtimet)){
580 JSLL_UI2L(local_time,PRMJ_MAX_UNIX_TIMET);
581 } else if(!JSLL_GE_ZERO(local_time)){
582 /*go ahead a day to make localtime work (does not work with 0) */
583 JSLL_UI2L(local_time,PRMJ_DAY_SECONDS);
586 #if defined(XP_WIN) && !defined(WINCE)
587 /* Windows does not follow POSIX. Updates to the
588 * TZ environment variable are not reflected
589 * immediately on that platform as they are
590 * on UNIX systems without this call.
592 _tzset();
593 #endif
595 JSLL_L2UI(local,local_time);
596 PRMJ_basetime(local_time,&prtm);
597 #ifndef HAVE_LOCALTIME_R
598 ptm = localtime(&local);
599 if(!ptm){
600 return 0;
602 tm = *ptm;
603 #else
604 localtime_r(&local,&tm); /* get dst information */
605 #endif
607 diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) +
608 ((tm.tm_min - prtm.tm_min) * 60);
610 if (diff < 0)
611 diff += PRMJ_DAY_SECONDS;
613 JSLL_UI2L(local_time,diff);
615 JSLL_MUL(local_time,local_time,us2s);
617 return(local_time);
620 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
621 static void
622 PRMJ_InvalidParameterHandler(const wchar_t *expression,
623 const wchar_t *function,
624 const wchar_t *file,
625 unsigned int line,
626 uintptr_t pReserved)
628 /* empty */
630 #endif
632 /* Format a time value into a buffer. Same semantics as strftime() */
633 size_t
634 PRMJ_FormatTime(char *buf, int buflen, const char *fmt, PRMJTime *prtm)
636 size_t result = 0;
637 #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
638 struct tm a;
639 int fake_tm_year = 0;
640 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
641 _invalid_parameter_handler oldHandler;
642 int oldReportMode;
643 #endif
645 /* Zero out the tm struct. Linux, SunOS 4 struct tm has extra members int
646 * tm_gmtoff, char *tm_zone; when tm_zone is garbage, strftime gets
647 * confused and dumps core. NSPR20 prtime.c attempts to fill these in by
648 * calling mktime on the partially filled struct, but this doesn't seem to
649 * work as well; the result string has "can't get timezone" for ECMA-valid
650 * years. Might still make sense to use this, but find the range of years
651 * for which valid tz information exists, and map (per ECMA hint) from the
652 * given year into that range.
654 * N.B. This hasn't been tested with anything that actually _uses_
655 * tm_gmtoff; zero might be the wrong thing to set it to if you really need
656 * to format a time. This fix is for jsdate.c, which only uses
657 * JS_FormatTime to get a string representing the time zone. */
658 memset(&a, 0, sizeof(struct tm));
660 a.tm_sec = prtm->tm_sec;
661 a.tm_min = prtm->tm_min;
662 a.tm_hour = prtm->tm_hour;
663 a.tm_mday = prtm->tm_mday;
664 a.tm_mon = prtm->tm_mon;
665 a.tm_wday = prtm->tm_wday;
667 #if defined(HAVE_LOCALTIME_R) && defined(HAVE_TM_ZONE_TM_GMTOFF)
669 struct tm td;
670 time_t bogus = 0;
671 localtime_r(&bogus, &td);
672 a.tm_gmtoff = td.tm_gmtoff;
673 a.tm_zone = td.tm_zone;
675 #endif
678 * Years before 1900 and after 9999 cause strftime() to abort on Windows.
679 * To avoid that we replace it with FAKE_YEAR_BASE + year % 100 and then
680 * replace matching substrings in the strftime() result with the real year.
681 * Note that FAKE_YEAR_BASE should be a multiple of 100 to make 2-digit
682 * year formats (%y) work correctly (since we won't find the fake year
683 * in that case).
684 * e.g. new Date(1873, 0).toLocaleFormat('%Y %y') => "1873 73"
685 * See bug 327869.
687 #define FAKE_YEAR_BASE 9900
688 if (prtm->tm_year < 1900 || prtm->tm_year > 9999) {
689 fake_tm_year = FAKE_YEAR_BASE + prtm->tm_year % 100;
690 a.tm_year = fake_tm_year - 1900;
692 else {
693 a.tm_year = prtm->tm_year - 1900;
695 a.tm_yday = prtm->tm_yday;
696 a.tm_isdst = prtm->tm_isdst;
699 * Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff
700 * are null. This doesn't quite work, though - the timezone is off by
701 * tzoff + dst. (And mktime seems to return -1 for the exact dst
702 * changeover time.)
705 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
706 oldHandler = _set_invalid_parameter_handler(PRMJ_InvalidParameterHandler);
707 oldReportMode = _CrtSetReportMode(_CRT_ASSERT, 0);
708 #endif
710 result = strftime(buf, buflen, fmt, &a);
712 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
713 _set_invalid_parameter_handler(oldHandler);
714 _CrtSetReportMode(_CRT_ASSERT, oldReportMode);
715 #endif
717 if (fake_tm_year && result) {
718 char real_year[16];
719 char fake_year[16];
720 size_t real_year_len;
721 size_t fake_year_len;
722 char* p;
724 sprintf(real_year, "%d", prtm->tm_year);
725 real_year_len = strlen(real_year);
726 sprintf(fake_year, "%d", fake_tm_year);
727 fake_year_len = strlen(fake_year);
729 /* Replace the fake year in the result with the real year. */
730 for (p = buf; (p = strstr(p, fake_year)); p += real_year_len) {
731 size_t new_result = result + real_year_len - fake_year_len;
732 if ((int)new_result >= buflen) {
733 return 0;
735 memmove(p + real_year_len, p + fake_year_len, strlen(p + fake_year_len));
736 memcpy(p, real_year, real_year_len);
737 result = new_result;
738 *(buf + result) = '\0';
741 #endif
742 return result;
745 /* table for number of days in a month */
746 static int mtab[] = {
747 /* jan, feb,mar,apr,may,jun */
748 31,28,31,30,31,30,
749 /* july,aug,sep,oct,nov,dec */
750 31,31,30,31,30,31
754 * basic time calculation functionality for localtime and gmtime
755 * setups up prtm argument with correct values based upon input number
756 * of seconds.
758 static void
759 PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm)
761 /* convert tsecs back to year,month,day,hour,secs */
762 JSInt32 year = 0;
763 JSInt32 month = 0;
764 JSInt32 yday = 0;
765 JSInt32 mday = 0;
766 JSInt32 wday = 6; /* start on a Sunday */
767 JSInt32 days = 0;
768 JSInt32 seconds = 0;
769 JSInt32 minutes = 0;
770 JSInt32 hours = 0;
771 JSInt32 isleap = 0;
773 /* Temporaries used for various computations */
774 JSInt64 result;
775 JSInt64 result1;
776 JSInt64 result2;
778 JSInt64 base;
780 /* Some variables for intermediate result storage to make computing isleap
781 easier/faster */
782 JSInt32 fourCenturyBlocks;
783 JSInt32 centuriesLeft;
784 JSInt32 fourYearBlocksLeft;
785 JSInt32 yearsLeft;
787 /* Since leap years work by 400/100/4 year intervals, precompute the length
788 of those in seconds if they start at the beginning of year 1. */
789 JSInt64 fourYears;
790 JSInt64 century;
791 JSInt64 fourCenturies;
793 JSLL_UI2L(result, PRMJ_DAY_SECONDS);
795 JSLL_I2L(fourYears, PRMJ_FOUR_YEARS_DAYS);
796 JSLL_MUL(fourYears, fourYears, result);
798 JSLL_I2L(century, PRMJ_CENTURY_DAYS);
799 JSLL_MUL(century, century, result);
801 JSLL_I2L(fourCenturies, PRMJ_FOUR_CENTURIES_DAYS);
802 JSLL_MUL(fourCenturies, fourCenturies, result);
804 /* get the base time via UTC */
805 base = PRMJ_ToExtendedTime(0);
806 JSLL_UI2L(result, PRMJ_USEC_PER_SEC);
807 JSLL_DIV(base,base,result);
808 JSLL_ADD(tsecs,tsecs,base);
810 /* Compute our |year|, |isleap|, and part of |days|. When this part is
811 done, |year| should hold the year our date falls in (number of whole
812 years elapsed before our date), isleap should hold 1 if the year the
813 date falls in is a leap year and 0 otherwise. */
815 /* First do year 0; it's special and nonleap. */
816 JSLL_UI2L(result, PRMJ_YEAR_SECONDS);
817 if (!JSLL_CMP(tsecs,<,result)) {
818 days = PRMJ_YEAR_DAYS;
819 year = 1;
820 JSLL_SUB(tsecs, tsecs, result);
823 /* Now use those constants we computed above */
824 JSLL_UDIVMOD(&result1, &result2, tsecs, fourCenturies);
825 JSLL_L2I(fourCenturyBlocks, result1);
826 year += fourCenturyBlocks * 400;
827 days += fourCenturyBlocks * PRMJ_FOUR_CENTURIES_DAYS;
828 tsecs = result2;
830 JSLL_UDIVMOD(&result1, &result2, tsecs, century);
831 JSLL_L2I(centuriesLeft, result1);
832 year += centuriesLeft * 100;
833 days += centuriesLeft * PRMJ_CENTURY_DAYS;
834 tsecs = result2;
836 JSLL_UDIVMOD(&result1, &result2, tsecs, fourYears);
837 JSLL_L2I(fourYearBlocksLeft, result1);
838 year += fourYearBlocksLeft * 4;
839 days += fourYearBlocksLeft * PRMJ_FOUR_YEARS_DAYS;
840 tsecs = result2;
842 /* Recall that |result| holds PRMJ_YEAR_SECONDS */
843 JSLL_UDIVMOD(&result1, &result2, tsecs, result);
844 JSLL_L2I(yearsLeft, result1);
845 year += yearsLeft;
846 days += yearsLeft * PRMJ_YEAR_DAYS;
847 tsecs = result2;
849 /* now compute isleap. Note that we don't have to use %, since we've
850 already computed those remainders. Also note that they're all offset by
851 1 because of the 1 for year 0. */
852 isleap =
853 (yearsLeft == 3) && (fourYearBlocksLeft != 24 || centuriesLeft == 3);
854 JS_ASSERT(isleap ==
855 ((year % 4 == 0) && (year % 100 != 0 || year % 400 == 0)));
857 JSLL_UI2L(result1,PRMJ_DAY_SECONDS);
859 JSLL_DIV(result,tsecs,result1);
860 JSLL_L2I(mday,result);
862 /* let's find the month */
863 while(((month == 1 && isleap) ?
864 (mday >= mtab[month] + 1) :
865 (mday >= mtab[month]))){
866 yday += mtab[month];
867 days += mtab[month];
869 mday -= mtab[month];
871 /* it's a Feb, check if this is a leap year */
872 if(month == 1 && isleap != 0){
873 yday++;
874 days++;
875 mday--;
877 month++;
880 /* now adjust tsecs */
881 JSLL_MUL(result,result,result1);
882 JSLL_SUB(tsecs,tsecs,result);
884 mday++; /* day of month always start with 1 */
885 days += mday;
886 wday = (days + wday) % 7;
888 yday += mday;
890 /* get the hours */
891 JSLL_UI2L(result1,PRMJ_HOUR_SECONDS);
892 JSLL_DIV(result,tsecs,result1);
893 JSLL_L2I(hours,result);
894 JSLL_MUL(result,result,result1);
895 JSLL_SUB(tsecs,tsecs,result);
897 /* get minutes */
898 JSLL_UI2L(result1,60);
899 JSLL_DIV(result,tsecs,result1);
900 JSLL_L2I(minutes,result);
901 JSLL_MUL(result,result,result1);
902 JSLL_SUB(tsecs,tsecs,result);
904 JSLL_L2I(seconds,tsecs);
906 prtm->tm_usec = 0L;
907 prtm->tm_sec = (JSInt8)seconds;
908 prtm->tm_min = (JSInt8)minutes;
909 prtm->tm_hour = (JSInt8)hours;
910 prtm->tm_mday = (JSInt8)mday;
911 prtm->tm_mon = (JSInt8)month;
912 prtm->tm_wday = (JSInt8)wday;
913 prtm->tm_year = (JSInt16)year;
914 prtm->tm_yday = (JSInt16)yday;