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 #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.
118 /* get the difference between this time zone and GMT */
119 memset((char *)<ime
,0,sizeof(ltime
));
122 return (JSInt32
)mktime(<ime
) - (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 */
134 PRMJ_ToExtendedTime(JSInt32 base_time
)
137 JSInt64 g1970GMTMicroSeconds
;
143 diff
= PRMJ_LocalGMTDifference();
144 JSLL_UI2L(tmp
, PRMJ_USEC_PER_SEC
);
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
);
160 #ifdef HAVE_SYSTEMTIMETOFILETIME
162 static const JSInt64 win2un
= JSLL_INIT(0x19DB1DE, 0xD53E8000);
164 #define FILETIME2INT64(ft) (((JSInt64)ft.dwHighDateTime) << 32LL | (JSInt64)ft.dwLowDateTime)
168 #if defined(HAVE_GETSYSTEMTIMEASFILETIME) || defined(HAVE_SYSTEMTIMETOFILETIME)
170 #if defined(HAVE_GETSYSTEMTIMEASFILETIME)
172 LowResTime(LPFILETIME lpft
)
174 GetSystemTimeAsFileTime(lpft
);
176 #elif defined(HAVE_SYSTEMTIMETOFILETIME)
178 LowResTime(LPFILETIME lpft
)
183 #error "No implementation of PRMJ_Now was selected."
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 */
197 CRITICAL_SECTION data_lock
;
198 CRITICAL_SECTION calibration_lock
;
205 static CalibrationData calibration
= { 0 };
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;
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. */
227 LowResTime(&ftStart
);
230 } while (memcmp(&ftStart
,&ft
, sizeof(ft
)) == 0);
234 calibration
.granularity
= (FILETIME2INT64(ft
) -
235 FILETIME2INT64(ftStart
))/10;
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
266 memset(&calibration
, 0, sizeof(calibration
));
269 InitializeCriticalSection(&calibration
.calibration_lock
);
270 InitializeCriticalSection(&calibration
.data_lock
);
272 InitializeCriticalSectionAndSpinCount(&calibration
.calibration_lock
, CALIBRATIONLOCK_SPINCOUNT
);
273 InitializeCriticalSectionAndSpinCount(&calibration
.data_lock
, DATALOCK_SPINCOUNT
);
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)
289 #define MUTEX_SETSPINCOUNT(m, c)
291 #define MUTEX_SETSPINCOUNT(m, c) SetCriticalSectionSpinCount((m),(c))
294 static PRCallOnceType calibrationOnce
= { 0 };
298 #define MUTEX_LOCK(m)
299 #define MUTEX_TRYLOCK(m) 1
300 #define MUTEX_UNLOCK(m)
301 #define MUTEX_SETSPINCOUNT(m, c)
305 #endif /* HAVE_GETSYSTEMTIMEASFILETIME */
312 JSInt64 s
, us
, ms2us
, s2us
;
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
);
326 #elif defined(XP_UNIX) || defined(XP_BEOS)
333 #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
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
);
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)
357 PRMJ_NowCalibration()
359 def NowCalibration():
360 expensive up-to-15ms call
364 needCalibration = False
367 PR_CallOnce(PRMJ_NowInit)
369 if not global.calibrated or needCalibration:
370 acquire calibration 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
380 set data mutex spin count = default
382 release calibration mutex
384 calculate lowres time
386 if highres timer available:
388 calculate highres time
389 cachedOffset = calibration.offset
390 highres time = calibration.last = max(highres time, calibration.last)
393 get kernel tick interval
395 if abs(highres - lowres) < kernel tick:
396 returnedTime = highres time
397 needCalibration = False
400 returnedTime = lowres
401 needCalibration = False
403 needCalibration = True
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;
418 static int nCalls
= 0;
419 long double lowresTime
, highresTimerValue
;
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
) {
434 return (FILETIME2INT64(ft
)-win2un
)/10L;
437 /* For non threadsafe platforms, NowInit is not necessary */
439 PR_CallOnce(&calibrationOnce
, NowInit
);
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);
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 */
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
);
492 /* Get an estimate of clock ticks per second from our own test */
493 skewThreshold
= calibration
.granularity
;
495 /* Rather than assume the NT kernel ticks every 15.6ms, ask it */
496 if (GetSystemTimeAdjustment(&timeAdjustment
,
498 &timeAdjustmentDisabled
)) {
499 if (timeAdjustmentDisabled
) {
500 /* timeAdjustment is in units of 100ns */
501 skewThreshold
= timeAdjustment
/10.0;
503 /* timeIncrement is in units of 100ns */
504 skewThreshold
= timeIncrement
/10.0;
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);*/
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
;
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
541 needsCalibration
= JS_TRUE
;
544 /* No detectable clock skew */
545 returnedTime
= (JSInt64
)highresTime
;
546 needsCalibration
= JS_FALSE
;
549 /* No high resolution timer is available, so fall back */
550 returnedTime
= (JSInt64
)lowresTime
;
552 } while (needsCalibration
);
558 /* Get the DST timezone offset for the time passed in */
560 PRMJ_DSTOffset(JSInt64 local_time
)
568 #ifndef HAVE_LOCALTIME_R
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.
595 JSLL_L2UI(local
,local_time
);
596 PRMJ_basetime(local_time
,&prtm
);
597 #ifndef HAVE_LOCALTIME_R
598 ptm
= localtime(&local
);
604 localtime_r(&local
,&tm
); /* get dst information */
607 diff
= ((tm
.tm_hour
- prtm
.tm_hour
) * PRMJ_HOUR_SECONDS
) +
608 ((tm
.tm_min
- prtm
.tm_min
) * 60);
611 diff
+= PRMJ_DAY_SECONDS
;
613 JSLL_UI2L(local_time
,diff
);
615 JSLL_MUL(local_time
,local_time
,us2s
);
620 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
622 PRMJ_InvalidParameterHandler(const wchar_t *expression
,
623 const wchar_t *function
,
632 /* Format a time value into a buffer. Same semantics as strftime() */
634 PRMJ_FormatTime(char *buf
, int buflen
, const char *fmt
, PRMJTime
*prtm
)
637 #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
639 int fake_tm_year
= 0;
640 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
641 _invalid_parameter_handler oldHandler
;
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)
671 localtime_r(&bogus
, &td
);
672 a
.tm_gmtoff
= td
.tm_gmtoff
;
673 a
.tm_zone
= td
.tm_zone
;
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
684 * e.g. new Date(1873, 0).toLocaleFormat('%Y %y') => "1873 73"
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;
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
705 #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
706 oldHandler
= _set_invalid_parameter_handler(PRMJ_InvalidParameterHandler
);
707 oldReportMode
= _CrtSetReportMode(_CRT_ASSERT
, 0);
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
);
717 if (fake_tm_year
&& result
) {
720 size_t real_year_len
;
721 size_t fake_year_len
;
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
) {
735 memmove(p
+ real_year_len
, p
+ fake_year_len
, strlen(p
+ fake_year_len
));
736 memcpy(p
, real_year
, real_year_len
);
738 *(buf
+ result
) = '\0';
745 /* table for number of days in a month */
746 static int mtab
[] = {
747 /* jan, feb,mar,apr,may,jun */
749 /* july,aug,sep,oct,nov,dec */
754 * basic time calculation functionality for localtime and gmtime
755 * setups up prtm argument with correct values based upon input number
759 PRMJ_basetime(JSInt64 tsecs
, PRMJTime
*prtm
)
761 /* convert tsecs back to year,month,day,hour,secs */
766 JSInt32 wday
= 6; /* start on a Sunday */
773 /* Temporaries used for various computations */
780 /* Some variables for intermediate result storage to make computing isleap
782 JSInt32 fourCenturyBlocks
;
783 JSInt32 centuriesLeft
;
784 JSInt32 fourYearBlocksLeft
;
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. */
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
;
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
;
830 JSLL_UDIVMOD(&result1
, &result2
, tsecs
, century
);
831 JSLL_L2I(centuriesLeft
, result1
);
832 year
+= centuriesLeft
* 100;
833 days
+= centuriesLeft
* PRMJ_CENTURY_DAYS
;
836 JSLL_UDIVMOD(&result1
, &result2
, tsecs
, fourYears
);
837 JSLL_L2I(fourYearBlocksLeft
, result1
);
838 year
+= fourYearBlocksLeft
* 4;
839 days
+= fourYearBlocksLeft
* PRMJ_FOUR_YEARS_DAYS
;
842 /* Recall that |result| holds PRMJ_YEAR_SECONDS */
843 JSLL_UDIVMOD(&result1
, &result2
, tsecs
, result
);
844 JSLL_L2I(yearsLeft
, result1
);
846 days
+= yearsLeft
* PRMJ_YEAR_DAYS
;
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. */
853 (yearsLeft
== 3) && (fourYearBlocksLeft
!= 24 || centuriesLeft
== 3);
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
]))){
871 /* it's a Feb, check if this is a leap year */
872 if(month
== 1 && isleap
!= 0){
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 */
886 wday
= (days
+ wday
) % 7;
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
);
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
);
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
;