Bug 449522 - Context menu for HTML5 <video> elements. r=gavin, ui-r=boriss
[wine-gecko.git] / js / src / prmjtime.cpp
blob881dd70e53f56bdb8a4a6891b12a56d30afb516f
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 #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"
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 /* 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);
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 */
121 #define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */
122 #define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */
124 /* Convert from base time to extended time */
125 static JSInt64
126 PRMJ_ToExtendedTime(JSInt32 base_time)
128 JSInt64 exttime;
129 JSInt64 g1970GMTMicroSeconds;
130 JSInt64 low;
131 JSInt32 diff;
132 JSInt64 tmp;
133 JSInt64 tmp1;
135 diff = PRMJ_LocalGMTDifference();
136 JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC);
137 JSLL_I2L(tmp1,diff);
138 JSLL_MUL(tmp,tmp,tmp1);
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);
150 JSLL_I2L(exttime,base_time);
151 JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds);
152 JSLL_SUB(exttime,exttime,tmp);
153 return exttime;
156 #ifdef XP_WIN
157 typedef struct CalibrationData
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' */
163 /* The last high res time that we returned since recalibrating */
164 JSInt64 last;
166 JSBool calibrated;
168 #ifdef JS_THREADSAFE
169 CRITICAL_SECTION data_lock;
170 CRITICAL_SECTION calibration_lock;
171 #endif
172 } CalibrationData;
174 static const JSInt64 win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000);
176 static CalibrationData calibration = { 0 };
178 #define FILETIME2INT64(ft) (((JSInt64)ft.dwHighDateTime) << 32LL | (JSInt64)ft.dwLowDateTime)
180 static void
181 NowCalibrate()
183 FILETIME ft, ftStart;
184 LARGE_INTEGER liFreq, now;
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;
194 if (calibration.freq > 0.0) {
195 JSInt64 calibrationDelta = 0;
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);
207 calibrationDelta = (FILETIME2INT64(ft) - FILETIME2INT64(ftStart))/10;
208 fprintf(stderr, "Calibration delta was %I64d us\n", calibrationDelta);
211 QueryPerformanceCounter(&now);
213 calibration.offset = (long double) FILETIME2INT64(ft);
214 calibration.timer_offset = (long double) now.QuadPart;
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;
223 calibration.calibrated = JS_TRUE;
227 #define CALIBRATIONLOCK_SPINCOUNT 0
228 #define DATALOCK_SPINCOUNT 4096
229 #define LASTLOCK_SPINCOUNT 4096
231 #ifdef JS_THREADSAFE
232 static PRStatus
233 NowInit(void)
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;
242 void
243 PRMJ_NowShutdown()
245 DeleteCriticalSection(&calibration.calibration_lock);
246 DeleteCriticalSection(&calibration.data_lock);
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))
254 static PRCallOnceType calibrationOnce = { 0 };
256 #else
258 #define MUTEX_LOCK(m)
259 #define MUTEX_TRYLOCK(m) 1
260 #define MUTEX_UNLOCK(m)
261 #define MUTEX_SETSPINCOUNT(m, c)
263 #endif
266 #endif /* XP_WIN */
270 Win32 python-esque pseudo code
271 Please see bug 363258 for why the win32 timing code is so complex.
273 calibration mutex : Win32CriticalSection(spincount=0)
274 data mutex : Win32CriticalSection(spincount=4096)
276 def NowInit():
277 init mutexes
278 PRMJ_NowCalibration()
280 def NowCalibration():
281 expensive up-to-15ms call
283 def PRMJ_Now():
284 returnedTime = 0
285 needCalibration = False
286 cachedOffset = 0.0
287 calibrated = False
288 PR_CallOnce(PRMJ_NowInit)
290 if not global.calibrated or needCalibration:
291 acquire calibration mutex
292 acquire data mutex
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
301 set data mutex spin count = default
302 release data mutex
303 release calibration mutex
305 calculate lowres time
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
314 get kernel tick interval
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
331 JSInt64
332 PRMJ_Now(void)
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 */
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
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;
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);
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);
392 NowCalibrate();
394 calibrated = JS_TRUE;
396 /* Restore spin count */
397 MUTEX_SETSPINCOUNT(&calibration.data_lock, DATALOCK_SPINCOUNT);
399 MUTEX_UNLOCK(&calibration.data_lock);
400 MUTEX_UNLOCK(&calibration.calibration_lock);
404 /* Calculate a low resolution time */
405 GetSystemTimeAsFileTime(&ft);
406 lowresTime = 0.1*(long double)(FILETIME2INT64(ft) - win2un);
408 if (calibration.freq > 0.0) {
409 long double highresTime, diff;
411 DWORD timeAdjustment, timeIncrement;
412 BOOL timeAdjustmentDisabled;
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;
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;
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);
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;
444 /* Check for clock skew */
445 diff = lowresTime - highresTime;
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);*/
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;
479 } else {
480 /* No detectable clock skew */
481 returnedTime = (JSInt64)highresTime;
482 needsCalibration = JS_FALSE;
484 } else {
485 /* No high resolution timer is available, so fall back */
486 returnedTime = (JSInt64)lowresTime;
488 } while (needsCalibration);
490 return returnedTime;
491 #endif
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 */
508 /* Get the DST timezone offset for the time passed in */
509 JSInt64
510 PRMJ_DSTOffset(JSInt64 local_time)
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
523 JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC);
524 JSLL_DIV(local_time, local_time, us2s);
526 /* get the maximum of time_t value */
527 JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET);
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);
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;
542 tm = *ptm;
543 #else
544 localtime_r(&local,&tm); /* get dst information */
545 #endif
547 diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) +
548 ((tm.tm_min - prtm.tm_min) * 60);
550 if(diff < 0){
551 diff += PRMJ_DAY_SECONDS;
554 JSLL_UI2L(local_time,diff);
556 JSLL_MUL(local_time,local_time,us2s);
558 return(local_time);
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)
569 /* empty */
571 #endif
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)
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
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.
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));
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;
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.
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;
623 else {
624 a.tm_year = prtm->tm_year - 1900;
626 a.tm_yday = prtm->tm_yday;
627 a.tm_isdst = prtm->tm_isdst;
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.)
636 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
637 oldHandler = _set_invalid_parameter_handler(PRMJ_InvalidParameterHandler);
638 oldReportMode = _CrtSetReportMode(_CRT_ASSERT, 0);
639 #endif
641 result = strftime(buf, buflen, fmt, &a);
643 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
644 _set_invalid_parameter_handler(oldHandler);
645 _CrtSetReportMode(_CRT_ASSERT, oldReportMode);
646 #endif
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;
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);
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;
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';
672 #endif
673 return result;
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
685 * basic time calculation functionality for localtime and gmtime
686 * setups up prtm argument with correct values based upon input number
687 * of seconds.
689 static void
690 PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm)
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;
704 /* Temporaries used for various computations */
705 JSInt64 result;
706 JSInt64 result1;
707 JSInt64 result2;
709 JSInt64 base;
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;
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;
724 JSLL_UI2L(result, PRMJ_DAY_SECONDS);
726 JSLL_I2L(fourYears, PRMJ_FOUR_YEARS_DAYS);
727 JSLL_MUL(fourYears, fourYears, result);
729 JSLL_I2L(century, PRMJ_CENTURY_DAYS);
730 JSLL_MUL(century, century, result);
732 JSLL_I2L(fourCenturies, PRMJ_FOUR_CENTURIES_DAYS);
733 JSLL_MUL(fourCenturies, fourCenturies, result);
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);
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. */
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);
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;
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;
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;
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;
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)));
788 JSLL_UI2L(result1,PRMJ_DAY_SECONDS);
790 JSLL_DIV(result,tsecs,result1);
791 JSLL_L2I(mday,result);
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];
800 mday -= mtab[month];
802 /* it's a Feb, check if this is a leap year */
803 if(month == 1 && isleap != 0){
804 yday++;
805 days++;
806 mday--;
808 month++;
811 /* now adjust tsecs */
812 JSLL_MUL(result,result,result1);
813 JSLL_SUB(tsecs,tsecs,result);
815 mday++; /* day of month always start with 1 */
816 days += mday;
817 wday = (days + wday) % 7;
819 yday += mday;
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);
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);
835 JSLL_L2I(seconds,tsecs);
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;