Save all modification
[mozilla-1.9/m8.git] / js / src / prmjtime.c
blob5926c59d3475f0ebde5f623ab3ee9a8f12f11888
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 */
67 #ifdef JS_THREADSAFE
68 #include <prinit.h>
69 #endif
71 #endif
73 #if defined(XP_UNIX) || defined(XP_BEOS)
75 #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
76 extern int gettimeofday(struct timeval *tv);
77 #endif
79 #include <sys/time.h>
81 #endif /* XP_UNIX */
83 #define PRMJ_YEAR_DAYS 365L
84 #define PRMJ_FOUR_YEARS_DAYS (4 * PRMJ_YEAR_DAYS + 1)
85 #define PRMJ_CENTURY_DAYS (25 * PRMJ_FOUR_YEARS_DAYS - 1)
86 #define PRMJ_FOUR_CENTURIES_DAYS (4 * PRMJ_CENTURY_DAYS + 1)
87 #define PRMJ_HOUR_SECONDS 3600L
88 #define PRMJ_DAY_SECONDS (24L * PRMJ_HOUR_SECONDS)
89 #define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * PRMJ_YEAR_DAYS)
90 #define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */
92 /* function prototypes */
93 static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm);
95 * get the difference in seconds between this time zone and UTC (GMT)
97 JSInt32
98 PRMJ_LocalGMTDifference()
100 struct tm ltime;
102 /* get the difference between this time zone and GMT */
103 memset((char *)&ltime,0,sizeof(ltime));
104 ltime.tm_mday = 2;
105 ltime.tm_year = 70;
106 #ifdef SUNOS4
107 ltime.tm_zone = 0;
108 ltime.tm_gmtoff = 0;
109 return timelocal(&ltime) - (24 * 3600);
110 #else
111 return (JSInt32)mktime(&ltime) - (24L * 3600L);
112 #endif
115 /* Constants for GMT offset from 1970 */
116 #define G1970GMTMICROHI 0x00dcdcad /* micro secs to 1970 hi */
117 #define G1970GMTMICROLOW 0x8b3fa000 /* micro secs to 1970 low */
119 #define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */
120 #define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */
122 /* Convert from base time to extended time */
123 static JSInt64
124 PRMJ_ToExtendedTime(JSInt32 base_time)
126 JSInt64 exttime;
127 JSInt64 g1970GMTMicroSeconds;
128 JSInt64 low;
129 JSInt32 diff;
130 JSInt64 tmp;
131 JSInt64 tmp1;
133 diff = PRMJ_LocalGMTDifference();
134 JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC);
135 JSLL_I2L(tmp1,diff);
136 JSLL_MUL(tmp,tmp,tmp1);
138 JSLL_UI2L(g1970GMTMicroSeconds,G1970GMTMICROHI);
139 JSLL_UI2L(low,G1970GMTMICROLOW);
140 #ifndef JS_HAVE_LONG_LONG
141 JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16);
142 JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16);
143 #else
144 JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,32);
145 #endif
146 JSLL_ADD(g1970GMTMicroSeconds,g1970GMTMicroSeconds,low);
148 JSLL_I2L(exttime,base_time);
149 JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds);
150 JSLL_SUB(exttime,exttime,tmp);
151 return exttime;
154 #ifdef XP_WIN
155 typedef struct CalibrationData
157 long double freq; /* The performance counter frequency */
158 long double offset; /* The low res 'epoch' */
159 long double timer_offset; /* The high res 'epoch' */
161 /* The last high res time that we returned since recalibrating */
162 JSInt64 last;
164 JSBool calibrated;
166 #ifdef JS_THREADSAFE
167 CRITICAL_SECTION data_lock;
168 CRITICAL_SECTION calibration_lock;
169 #endif
170 } CalibrationData;
172 static const JSInt64 win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000);
174 static CalibrationData calibration = { 0 };
176 #define FILETIME2INT64(ft) (((JSInt64)ft.dwHighDateTime) << 32LL | (JSInt64)ft.dwLowDateTime)
178 static void
179 NowCalibrate()
181 FILETIME ft, ftStart;
182 LARGE_INTEGER liFreq, now;
184 if (calibration.freq == 0.0) {
185 if(!QueryPerformanceFrequency(&liFreq)) {
186 /* High-performance timer is unavailable */
187 calibration.freq = -1.0;
188 } else {
189 calibration.freq = (long double) liFreq.QuadPart;
192 if (calibration.freq > 0.0) {
193 JSInt64 calibrationDelta = 0;
195 /* By wrapping a timeBegin/EndPeriod pair of calls around this loop,
196 the loop seems to take much less time (1 ms vs 15ms) on Vista. */
197 timeBeginPeriod(1);
198 GetSystemTimeAsFileTime(&ftStart);
199 do {
200 GetSystemTimeAsFileTime(&ft);
201 } while (memcmp(&ftStart,&ft, sizeof(ft)) == 0);
202 timeEndPeriod(1);
205 calibrationDelta = (FILETIME2INT64(ft) - FILETIME2INT64(ftStart))/10;
206 fprintf(stderr, "Calibration delta was %I64d us\n", calibrationDelta);
209 QueryPerformanceCounter(&now);
211 calibration.offset = (long double) FILETIME2INT64(ft);
212 calibration.timer_offset = (long double) now.QuadPart;
214 /* The windows epoch is around 1600. The unix epoch is around
215 1970. win2un is the difference (in windows time units which
216 are 10 times more highres than the JS time unit) */
217 calibration.offset -= win2un;
218 calibration.offset *= 0.1;
219 calibration.last = 0;
221 calibration.calibrated = JS_TRUE;
225 #define CALIBRATIONLOCK_SPINCOUNT 0
226 #define DATALOCK_SPINCOUNT 4096
227 #define LASTLOCK_SPINCOUNT 4096
229 #ifdef JS_THREADSAFE
230 static PRStatus PR_CALLBACK
231 NowInit(void)
233 memset(&calibration, 0, sizeof(calibration));
234 NowCalibrate();
235 InitializeCriticalSectionAndSpinCount(&calibration.calibration_lock, CALIBRATIONLOCK_SPINCOUNT);
236 InitializeCriticalSectionAndSpinCount(&calibration.data_lock, DATALOCK_SPINCOUNT);
237 return PR_SUCCESS;
240 void
241 PRMJ_NowShutdown()
243 DeleteCriticalSection(&calibration.calibration_lock);
244 DeleteCriticalSection(&calibration.data_lock);
247 #define MUTEX_LOCK(m) EnterCriticalSection(m)
248 #define MUTEX_TRYLOCK(m) TryEnterCriticalSection(m)
249 #define MUTEX_UNLOCK(m) LeaveCriticalSection(m)
250 #define MUTEX_SETSPINCOUNT(m, c) SetCriticalSectionSpinCount((m),(c))
252 static PRCallOnceType calibrationOnce = { 0 };
254 #else
256 #define MUTEX_LOCK(m)
257 #define MUTEX_TRYLOCK(m) 1
258 #define MUTEX_UNLOCK(m)
259 #define MUTEX_SETSPINCOUNT(m, c)
261 #endif
264 #endif // XP_WIN
268 Win32 python-esque pseudo code
269 Please see bug 363258 for why the win32 timing code is so complex.
271 calibration mutex : Win32CriticalSection(spincount=0)
272 data mutex : Win32CriticalSection(spincount=4096)
274 def NowInit():
275 init mutexes
276 PRMJ_NowCalibration()
278 def NowCalibration():
279 expensive up-to-15ms call
281 def PRMJ_Now():
282 returnedTime = 0
283 needCalibration = False
284 cachedOffset = 0.0
285 calibrated = False
286 PR_CallOnce(PRMJ_NowInit)
288 if not global.calibrated or needCalibration:
289 acquire calibration mutex
290 acquire data mutex
292 // Only recalibrate if someone didn't already
293 if cachedOffset == calibration.offset:
294 // Have all waiting threads immediately wait
295 set data mutex spin count = 0
296 PRMJ_NowCalibrate()
297 calibrated = 1
299 set data mutex spin count = default
300 release data mutex
301 release calibration mutex
303 calculate lowres time
305 if highres timer available:
306 acquire data mutex
307 calculate highres time
308 cachedOffset = calibration.offset
309 highres time = calibration.last = max(highres time, calibration.last)
310 release data mutex
312 get kernel tick interval
314 if abs(highres - lowres) < kernel tick:
315 returnedTime = highres time
316 needCalibration = False
317 else:
318 if calibrated:
319 returnedTime = lowres
320 needCalibration = False
321 else:
322 needCalibration = True
323 else:
324 returnedTime = lowres
325 while needCalibration
329 JSInt64
330 PRMJ_Now(void)
332 #ifdef XP_OS2
333 JSInt64 s, us, ms2us, s2us;
334 struct timeb b;
335 #endif
336 #ifdef XP_WIN
337 static int nCalls = 0;
338 long double lowresTime, highresTimerValue;
339 FILETIME ft;
340 LARGE_INTEGER now;
341 JSBool calibrated = JS_FALSE;
342 JSBool needsCalibration = JS_FALSE;
343 JSInt64 returnedTime;
344 long double cachedOffset = 0.0;
345 #endif
346 #if defined(XP_UNIX) || defined(XP_BEOS)
347 struct timeval tv;
348 JSInt64 s, us, s2us;
349 #endif /* XP_UNIX */
351 #ifdef XP_OS2
352 ftime(&b);
353 JSLL_UI2L(ms2us, PRMJ_USEC_PER_MSEC);
354 JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC);
355 JSLL_UI2L(s, b.time);
356 JSLL_UI2L(us, b.millitm);
357 JSLL_MUL(us, us, ms2us);
358 JSLL_MUL(s, s, s2us);
359 JSLL_ADD(s, s, us);
360 return s;
361 #endif
362 #ifdef XP_WIN
364 /* To avoid regressing startup time (where high resolution is likely
365 not needed), give the old behavior for the first few calls.
366 This does not appear to be needed on Vista as the timeBegin/timeEndPeriod
367 calls seem to immediately take effect. */
368 int thiscall = JS_ATOMIC_INCREMENT(&nCalls);
369 /* 10 seems to be the number of calls to load with a blank homepage */
370 if (thiscall <= 10) {
371 GetSystemTimeAsFileTime(&ft);
372 return (FILETIME2INT64(ft)-win2un)/10L;
375 /* For non threadsafe platforms, NowInit is not necessary */
376 #ifdef JS_THREADSAFE
377 PR_CallOnce(&calibrationOnce, NowInit);
378 #endif
379 do {
380 if (!calibration.calibrated || needsCalibration) {
381 MUTEX_LOCK(&calibration.calibration_lock);
382 MUTEX_LOCK(&calibration.data_lock);
384 /* Recalibrate only if no one else did before us */
385 if(calibration.offset == cachedOffset) {
386 /* Since calibration can take a while, make any other
387 threads immediately wait */
388 MUTEX_SETSPINCOUNT(&calibration.data_lock, 0);
390 NowCalibrate();
392 calibrated = JS_TRUE;
394 /* Restore spin count */
395 MUTEX_SETSPINCOUNT(&calibration.data_lock, DATALOCK_SPINCOUNT);
397 MUTEX_UNLOCK(&calibration.data_lock);
398 MUTEX_UNLOCK(&calibration.calibration_lock);
402 /* Calculate a low resolution time */
403 GetSystemTimeAsFileTime(&ft);
404 lowresTime = 0.1*(long double)(FILETIME2INT64(ft) - win2un);
406 if (calibration.freq > 0.0) {
407 long double highresTime, diff;
409 DWORD timeAdjustment, timeIncrement;
410 BOOL timeAdjustmentDisabled;
412 /* Default to 15.625 ms if the syscall fails */
413 long double skewThreshold = 15625.25;
414 /* Grab high resolution time */
415 QueryPerformanceCounter(&now);
416 highresTimerValue = (long double)now.QuadPart;
418 MUTEX_LOCK(&calibration.data_lock);
419 highresTime = calibration.offset + PRMJ_USEC_PER_SEC*
420 (highresTimerValue-calibration.timer_offset)/calibration.freq;
421 cachedOffset = calibration.offset;
423 /* On some dual processor/core systems, we might get an earlier time
424 so we cache the last time that we returned */
425 calibration.last = max(calibration.last,(JSInt64)highresTime);
426 returnedTime = calibration.last;
427 MUTEX_UNLOCK(&calibration.data_lock);
429 /* Rather than assume the NT kernel ticks every 15.6ms, ask it */
430 if (GetSystemTimeAdjustment(&timeAdjustment,
431 &timeIncrement,
432 &timeAdjustmentDisabled)) {
433 if (timeAdjustmentDisabled) {
434 /* timeAdjustment is in units of 100ns */
435 skewThreshold = timeAdjustment/10.0;
436 } else {
437 /* timeIncrement is in units of 100ns */
438 skewThreshold = timeIncrement/10.0;
442 /* Check for clock skew */
443 diff = lowresTime - highresTime;
445 /* For some reason that I have not determined, the skew can be
446 up to twice a kernel tick. This does not seem to happen by
447 itself, but I have only seen it triggered by another program
448 doing some kind of file I/O. The symptoms are a negative diff
449 followed by an equally large positive diff. */
450 if (fabs(diff) > 2*skewThreshold) {
451 //fprintf(stderr,"Clock skew detected (diff = %f)!\n", diff);
453 if (calibrated) {
454 /* If we already calibrated once this instance, and the
455 clock is still skewed, then either the processor(s) are
456 wildly changing clockspeed or the system is so busy that
457 we get switched out for long periods of time. In either
458 case, it would be infeasible to make use of high
459 resolution results for anything, so let's resort to old
460 behavior for this call. It's possible that in the
461 future, the user will want the high resolution timer, so
462 we don't disable it entirely. */
463 returnedTime = (JSInt64)lowresTime;
464 needsCalibration = JS_FALSE;
465 } else {
466 /* It is possible that when we recalibrate, we will return a
467 value less than what we have returned before; this is
468 unavoidable. We cannot tell the different between a
469 faulty QueryPerformanceCounter implementation and user
470 changes to the operating system time. Since we must
471 respect user changes to the operating system time, we
472 cannot maintain the invariant that Date.now() never
473 decreases; the old implementation has this behavior as
474 well. */
475 needsCalibration = JS_TRUE;
477 } else {
478 /* No detectable clock skew */
479 returnedTime = (JSInt64)highresTime;
480 needsCalibration = JS_FALSE;
482 } else {
483 /* No high resolution timer is available, so fall back */
484 returnedTime = (JSInt64)lowresTime;
486 } while (needsCalibration);
488 return returnedTime;
489 #endif
491 #if defined(XP_UNIX) || defined(XP_BEOS)
492 #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
493 gettimeofday(&tv);
494 #else
495 gettimeofday(&tv, 0);
496 #endif /* _SVID_GETTOD */
497 JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC);
498 JSLL_UI2L(s, tv.tv_sec);
499 JSLL_UI2L(us, tv.tv_usec);
500 JSLL_MUL(s, s, s2us);
501 JSLL_ADD(s, s, us);
502 return s;
503 #endif /* XP_UNIX */
506 /* Get the DST timezone offset for the time passed in */
507 JSInt64
508 PRMJ_DSTOffset(JSInt64 local_time)
510 JSInt64 us2s;
511 time_t local;
512 JSInt32 diff;
513 JSInt64 maxtimet;
514 struct tm tm;
515 PRMJTime prtm;
516 #ifndef HAVE_LOCALTIME_R
517 struct tm *ptm;
518 #endif
521 JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC);
522 JSLL_DIV(local_time, local_time, us2s);
524 /* get the maximum of time_t value */
525 JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET);
527 if(JSLL_CMP(local_time,>,maxtimet)){
528 JSLL_UI2L(local_time,PRMJ_MAX_UNIX_TIMET);
529 } else if(!JSLL_GE_ZERO(local_time)){
530 /*go ahead a day to make localtime work (does not work with 0) */
531 JSLL_UI2L(local_time,PRMJ_DAY_SECONDS);
533 JSLL_L2UI(local,local_time);
534 PRMJ_basetime(local_time,&prtm);
535 #ifndef HAVE_LOCALTIME_R
536 ptm = localtime(&local);
537 if(!ptm){
538 return JSLL_ZERO;
540 tm = *ptm;
541 #else
542 localtime_r(&local,&tm); /* get dst information */
543 #endif
545 diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) +
546 ((tm.tm_min - prtm.tm_min) * 60);
548 if(diff < 0){
549 diff += PRMJ_DAY_SECONDS;
552 JSLL_UI2L(local_time,diff);
554 JSLL_MUL(local_time,local_time,us2s);
556 return(local_time);
559 /* Format a time value into a buffer. Same semantics as strftime() */
560 size_t
561 PRMJ_FormatTime(char *buf, int buflen, const char *fmt, PRMJTime *prtm)
563 #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
564 struct tm a;
566 /* Zero out the tm struct. Linux, SunOS 4 struct tm has extra members int
567 * tm_gmtoff, char *tm_zone; when tm_zone is garbage, strftime gets
568 * confused and dumps core. NSPR20 prtime.c attempts to fill these in by
569 * calling mktime on the partially filled struct, but this doesn't seem to
570 * work as well; the result string has "can't get timezone" for ECMA-valid
571 * years. Might still make sense to use this, but find the range of years
572 * for which valid tz information exists, and map (per ECMA hint) from the
573 * given year into that range.
575 * N.B. This hasn't been tested with anything that actually _uses_
576 * tm_gmtoff; zero might be the wrong thing to set it to if you really need
577 * to format a time. This fix is for jsdate.c, which only uses
578 * JS_FormatTime to get a string representing the time zone. */
579 memset(&a, 0, sizeof(struct tm));
581 a.tm_sec = prtm->tm_sec;
582 a.tm_min = prtm->tm_min;
583 a.tm_hour = prtm->tm_hour;
584 a.tm_mday = prtm->tm_mday;
585 a.tm_mon = prtm->tm_mon;
586 a.tm_wday = prtm->tm_wday;
587 a.tm_year = prtm->tm_year - 1900;
588 a.tm_yday = prtm->tm_yday;
589 a.tm_isdst = prtm->tm_isdst;
591 /* Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff
592 * are null. This doesn't quite work, though - the timezone is off by
593 * tzoff + dst. (And mktime seems to return -1 for the exact dst
594 * changeover time.)
598 #if defined(SUNOS4)
599 if (mktime(&a) == -1) {
600 /* Seems to fail whenever the requested date is outside of the 32-bit
601 * UNIX epoch. We could proceed at this point (setting a.tm_zone to
602 * "") but then strftime returns a string with a 2-digit field of
603 * garbage for the year. So we return 0 and hope jsdate.c
604 * will fall back on toString.
606 return 0;
608 #endif
610 return strftime(buf, buflen, fmt, &a);
611 #endif
614 /* table for number of days in a month */
615 static int mtab[] = {
616 /* jan, feb,mar,apr,may,jun */
617 31,28,31,30,31,30,
618 /* july,aug,sep,oct,nov,dec */
619 31,31,30,31,30,31
623 * basic time calculation functionality for localtime and gmtime
624 * setups up prtm argument with correct values based upon input number
625 * of seconds.
627 static void
628 PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm)
630 /* convert tsecs back to year,month,day,hour,secs */
631 JSInt32 year = 0;
632 JSInt32 month = 0;
633 JSInt32 yday = 0;
634 JSInt32 mday = 0;
635 JSInt32 wday = 6; /* start on a Sunday */
636 JSInt32 days = 0;
637 JSInt32 seconds = 0;
638 JSInt32 minutes = 0;
639 JSInt32 hours = 0;
640 JSInt32 isleap = 0;
642 /* Temporaries used for various computations */
643 JSInt64 result;
644 JSInt64 result1;
645 JSInt64 result2;
647 JSInt64 base;
649 /* Some variables for intermediate result storage to make computing isleap
650 easier/faster */
651 JSInt32 fourCenturyBlocks;
652 JSInt32 centuriesLeft;
653 JSInt32 fourYearBlocksLeft;
654 JSInt32 yearsLeft;
656 /* Since leap years work by 400/100/4 year intervals, precompute the length
657 of those in seconds if they start at the beginning of year 1. */
658 JSInt64 fourYears;
659 JSInt64 century;
660 JSInt64 fourCenturies;
662 JSLL_UI2L(result, PRMJ_DAY_SECONDS);
664 JSLL_I2L(fourYears, PRMJ_FOUR_YEARS_DAYS);
665 JSLL_MUL(fourYears, fourYears, result);
667 JSLL_I2L(century, PRMJ_CENTURY_DAYS);
668 JSLL_MUL(century, century, result);
670 JSLL_I2L(fourCenturies, PRMJ_FOUR_CENTURIES_DAYS);
671 JSLL_MUL(fourCenturies, fourCenturies, result);
673 /* get the base time via UTC */
674 base = PRMJ_ToExtendedTime(0);
675 JSLL_UI2L(result, PRMJ_USEC_PER_SEC);
676 JSLL_DIV(base,base,result);
677 JSLL_ADD(tsecs,tsecs,base);
679 /* Compute our |year|, |isleap|, and part of |days|. When this part is
680 done, |year| should hold the year our date falls in (number of whole
681 years elapsed before our date), isleap should hold 1 if the year the
682 date falls in is a leap year and 0 otherwise. */
684 /* First do year 0; it's special and nonleap. */
685 JSLL_UI2L(result, PRMJ_YEAR_SECONDS);
686 if (!JSLL_CMP(tsecs,<,result)) {
687 days = PRMJ_YEAR_DAYS;
688 year = 1;
689 JSLL_SUB(tsecs, tsecs, result);
692 /* Now use those constants we computed above */
693 JSLL_UDIVMOD(&result1, &result2, tsecs, fourCenturies);
694 JSLL_L2I(fourCenturyBlocks, result1);
695 year += fourCenturyBlocks * 400;
696 days += fourCenturyBlocks * PRMJ_FOUR_CENTURIES_DAYS;
697 tsecs = result2;
699 JSLL_UDIVMOD(&result1, &result2, tsecs, century);
700 JSLL_L2I(centuriesLeft, result1);
701 year += centuriesLeft * 100;
702 days += centuriesLeft * PRMJ_CENTURY_DAYS;
703 tsecs = result2;
705 JSLL_UDIVMOD(&result1, &result2, tsecs, fourYears);
706 JSLL_L2I(fourYearBlocksLeft, result1);
707 year += fourYearBlocksLeft * 4;
708 days += fourYearBlocksLeft * PRMJ_FOUR_YEARS_DAYS;
709 tsecs = result2;
711 /* Recall that |result| holds PRMJ_YEAR_SECONDS */
712 JSLL_UDIVMOD(&result1, &result2, tsecs, result);
713 JSLL_L2I(yearsLeft, result1);
714 year += yearsLeft;
715 days += yearsLeft * PRMJ_YEAR_DAYS;
716 tsecs = result2;
718 /* now compute isleap. Note that we don't have to use %, since we've
719 already computed those remainders. Also note that they're all offset by
720 1 because of the 1 for year 0. */
721 isleap =
722 (yearsLeft == 3) && (fourYearBlocksLeft != 24 || centuriesLeft == 3);
723 JS_ASSERT(isleap ==
724 ((year % 4 == 0) && (year % 100 != 0 || year % 400 == 0)));
726 JSLL_UI2L(result1,PRMJ_DAY_SECONDS);
728 JSLL_DIV(result,tsecs,result1);
729 JSLL_L2I(mday,result);
731 /* let's find the month */
732 while(((month == 1 && isleap) ?
733 (mday >= mtab[month] + 1) :
734 (mday >= mtab[month]))){
735 yday += mtab[month];
736 days += mtab[month];
738 mday -= mtab[month];
740 /* it's a Feb, check if this is a leap year */
741 if(month == 1 && isleap != 0){
742 yday++;
743 days++;
744 mday--;
746 month++;
749 /* now adjust tsecs */
750 JSLL_MUL(result,result,result1);
751 JSLL_SUB(tsecs,tsecs,result);
753 mday++; /* day of month always start with 1 */
754 days += mday;
755 wday = (days + wday) % 7;
757 yday += mday;
759 /* get the hours */
760 JSLL_UI2L(result1,PRMJ_HOUR_SECONDS);
761 JSLL_DIV(result,tsecs,result1);
762 JSLL_L2I(hours,result);
763 JSLL_MUL(result,result,result1);
764 JSLL_SUB(tsecs,tsecs,result);
766 /* get minutes */
767 JSLL_UI2L(result1,60);
768 JSLL_DIV(result,tsecs,result1);
769 JSLL_L2I(minutes,result);
770 JSLL_MUL(result,result,result1);
771 JSLL_SUB(tsecs,tsecs,result);
773 JSLL_L2I(seconds,tsecs);
775 prtm->tm_usec = 0L;
776 prtm->tm_sec = (JSInt8)seconds;
777 prtm->tm_min = (JSInt8)minutes;
778 prtm->tm_hour = (JSInt8)hours;
779 prtm->tm_mday = (JSInt8)mday;
780 prtm->tm_mon = (JSInt8)month;
781 prtm->tm_wday = (JSInt8)wday;
782 prtm->tm_year = (JSInt16)year;
783 prtm->tm_yday = (JSInt16)yday;