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
16 * The Original Code is Mozilla Communicator client code, released
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.
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 ***** */
56 #define PRMJ_DO_MILLISECONDS 1
59 #include <sys/timeb.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
70 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
71 #include <stdlib.h> /* for _set_invalid_parameter_handler */
72 #include <crtdbg.h> /* for _CrtSetReportMode */
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
);
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)
106 PRMJ_LocalGMTDifference()
110 /* get the difference between this time zone and GMT */
111 memset((char *)<ime
,0,sizeof(ltime
));
114 return (JSInt32
)mktime(<ime
) - (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 */
126 PRMJ_ToExtendedTime(JSInt32 base_time
)
129 JSInt64 g1970GMTMicroSeconds
;
135 diff
= PRMJ_LocalGMTDifference();
136 JSLL_UI2L(tmp
, PRMJ_USEC_PER_SEC
);
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);
146 JSLL_SHL(g1970GMTMicroSeconds
,g1970GMTMicroSeconds
,32);
148 JSLL_ADD(g1970GMTMicroSeconds
,g1970GMTMicroSeconds
,low
);
150 JSLL_I2L(exttime
,base_time
);
151 JSLL_ADD(exttime
,exttime
,g1970GMTMicroSeconds
);
152 JSLL_SUB(exttime
,exttime
,tmp
);
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 */
169 CRITICAL_SECTION data_lock
;
170 CRITICAL_SECTION calibration_lock
;
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)
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;
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. */
200 GetSystemTimeAsFileTime(&ftStart
);
202 GetSystemTimeAsFileTime(&ft
);
203 } while (memcmp(&ftStart
,&ft
, sizeof(ft
)) == 0);
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
235 memset(&calibration
, 0, sizeof(calibration
));
237 InitializeCriticalSectionAndSpinCount(&calibration
.calibration_lock
, CALIBRATIONLOCK_SPINCOUNT
);
238 InitializeCriticalSectionAndSpinCount(&calibration
.data_lock
, DATALOCK_SPINCOUNT
);
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 };
258 #define MUTEX_LOCK(m)
259 #define MUTEX_TRYLOCK(m) 1
260 #define MUTEX_UNLOCK(m)
261 #define MUTEX_SETSPINCOUNT(m, c)
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)
278 PRMJ_NowCalibration()
280 def NowCalibration():
281 expensive up-to-15ms call
285 needCalibration = False
288 PR_CallOnce(PRMJ_NowInit)
290 if not global.calibrated or needCalibration:
291 acquire calibration 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
301 set data mutex spin count = default
303 release calibration mutex
305 calculate lowres time
307 if highres timer available:
309 calculate highres time
310 cachedOffset = calibration.offset
311 highres time = calibration.last = max(highres time, calibration.last)
314 get kernel tick interval
316 if abs(highres - lowres) < kernel tick:
317 returnedTime = highres time
318 needCalibration = False
321 returnedTime = lowres
322 needCalibration = False
324 needCalibration = True
326 returnedTime = lowres
327 while needCalibration
335 JSInt64 s
, us
, ms2us
, s2us
;
339 static int nCalls
= 0;
340 long double lowresTime
, highresTimerValue
;
343 JSBool calibrated
= JS_FALSE
;
344 JSBool needsCalibration
= JS_FALSE
;
345 JSInt64 returnedTime
;
346 long double cachedOffset
= 0.0;
348 #if defined(XP_UNIX) || defined(XP_BEOS)
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
);
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 */
379 PR_CallOnce(&calibrationOnce
, NowInit
);
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);
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
,
434 &timeAdjustmentDisabled
)) {
435 if (timeAdjustmentDisabled
) {
436 /* timeAdjustment is in units of 100ns */
437 skewThreshold
= timeAdjustment
/10.0;
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);*/
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
;
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
477 needsCalibration
= JS_TRUE
;
480 /* No detectable clock skew */
481 returnedTime
= (JSInt64
)highresTime
;
482 needsCalibration
= JS_FALSE
;
485 /* No high resolution timer is available, so fall back */
486 returnedTime
= (JSInt64
)lowresTime
;
488 } while (needsCalibration
);
493 #if defined(XP_UNIX) || defined(XP_BEOS)
494 #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
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
);
508 /* Get the DST timezone offset for the time passed in */
510 PRMJ_DSTOffset(JSInt64 local_time
)
518 #ifndef HAVE_LOCALTIME_R
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
);
544 localtime_r(&local
,&tm
); /* get dst information */
547 diff
= ((tm
.tm_hour
- prtm
.tm_hour
) * PRMJ_HOUR_SECONDS
) +
548 ((tm
.tm_min
- prtm
.tm_min
) * 60);
551 diff
+= PRMJ_DAY_SECONDS
;
554 JSLL_UI2L(local_time
,diff
);
556 JSLL_MUL(local_time
,local_time
,us2s
);
561 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
563 PRMJ_InvalidParameterHandler(const wchar_t *expression
,
564 const wchar_t *function
,
573 /* Format a time value into a buffer. Same semantics as strftime() */
575 PRMJ_FormatTime(char *buf
, int buflen
, const char *fmt
, PRMJTime
*prtm
)
578 #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
580 int fake_tm_year
= 0;
581 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
582 _invalid_parameter_handler oldHandler
;
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
615 * e.g. new Date(1873, 0).toLocaleFormat('%Y %y') => "1873 73"
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;
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
636 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
637 oldHandler
= _set_invalid_parameter_handler(PRMJ_InvalidParameterHandler
);
638 oldReportMode
= _CrtSetReportMode(_CRT_ASSERT
, 0);
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
);
648 if (fake_tm_year
&& result
) {
651 size_t real_year_len
;
652 size_t fake_year_len
;
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
) {
666 memmove(p
+ real_year_len
, p
+ fake_year_len
, strlen(p
+ fake_year_len
));
667 memcpy(p
, real_year
, real_year_len
);
669 *(buf
+ result
) = '\0';
676 /* table for number of days in a month */
677 static int mtab
[] = {
678 /* jan, feb,mar,apr,may,jun */
680 /* july,aug,sep,oct,nov,dec */
685 * basic time calculation functionality for localtime and gmtime
686 * setups up prtm argument with correct values based upon input number
690 PRMJ_basetime(JSInt64 tsecs
, PRMJTime
*prtm
)
692 /* convert tsecs back to year,month,day,hour,secs */
697 JSInt32 wday
= 6; /* start on a Sunday */
704 /* Temporaries used for various computations */
711 /* Some variables for intermediate result storage to make computing isleap
713 JSInt32 fourCenturyBlocks
;
714 JSInt32 centuriesLeft
;
715 JSInt32 fourYearBlocksLeft
;
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. */
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
;
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
;
761 JSLL_UDIVMOD(&result1
, &result2
, tsecs
, century
);
762 JSLL_L2I(centuriesLeft
, result1
);
763 year
+= centuriesLeft
* 100;
764 days
+= centuriesLeft
* PRMJ_CENTURY_DAYS
;
767 JSLL_UDIVMOD(&result1
, &result2
, tsecs
, fourYears
);
768 JSLL_L2I(fourYearBlocksLeft
, result1
);
769 year
+= fourYearBlocksLeft
* 4;
770 days
+= fourYearBlocksLeft
* PRMJ_FOUR_YEARS_DAYS
;
773 /* Recall that |result| holds PRMJ_YEAR_SECONDS */
774 JSLL_UDIVMOD(&result1
, &result2
, tsecs
, result
);
775 JSLL_L2I(yearsLeft
, result1
);
777 days
+= yearsLeft
* PRMJ_YEAR_DAYS
;
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. */
784 (yearsLeft
== 3) && (fourYearBlocksLeft
!= 24 || centuriesLeft
== 3);
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
]))){
802 /* it's a Feb, check if this is a leap year */
803 if(month
== 1 && isleap
!= 0){
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 */
817 wday
= (days
+ wday
) % 7;
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
);
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
);
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
;