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 */
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
);
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)
98 PRMJ_LocalGMTDifference()
102 /* get the difference between this time zone and GMT */
103 memset((char *)<ime
,0,sizeof(ltime
));
109 return timelocal(<ime
) - (24 * 3600);
111 return (JSInt32
)mktime(<ime
) - (24L * 3600L);
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 */
124 PRMJ_ToExtendedTime(JSInt32 base_time
)
127 JSInt64 g1970GMTMicroSeconds
;
133 diff
= PRMJ_LocalGMTDifference();
134 JSLL_UI2L(tmp
, PRMJ_USEC_PER_SEC
);
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);
144 JSLL_SHL(g1970GMTMicroSeconds
,g1970GMTMicroSeconds
,32);
146 JSLL_ADD(g1970GMTMicroSeconds
,g1970GMTMicroSeconds
,low
);
148 JSLL_I2L(exttime
,base_time
);
149 JSLL_ADD(exttime
,exttime
,g1970GMTMicroSeconds
);
150 JSLL_SUB(exttime
,exttime
,tmp
);
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 */
167 CRITICAL_SECTION data_lock
;
168 CRITICAL_SECTION calibration_lock
;
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)
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;
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. */
198 GetSystemTimeAsFileTime(&ftStart
);
200 GetSystemTimeAsFileTime(&ft
);
201 } while (memcmp(&ftStart
,&ft
, sizeof(ft
)) == 0);
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
230 static PRStatus PR_CALLBACK
233 memset(&calibration
, 0, sizeof(calibration
));
235 InitializeCriticalSectionAndSpinCount(&calibration
.calibration_lock
, CALIBRATIONLOCK_SPINCOUNT
);
236 InitializeCriticalSectionAndSpinCount(&calibration
.data_lock
, DATALOCK_SPINCOUNT
);
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 };
256 #define MUTEX_LOCK(m)
257 #define MUTEX_TRYLOCK(m) 1
258 #define MUTEX_UNLOCK(m)
259 #define MUTEX_SETSPINCOUNT(m, c)
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)
276 PRMJ_NowCalibration()
278 def NowCalibration():
279 expensive up-to-15ms call
283 needCalibration = False
286 PR_CallOnce(PRMJ_NowInit)
288 if not global.calibrated or needCalibration:
289 acquire calibration 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
299 set data mutex spin count = default
301 release calibration mutex
303 calculate lowres time
305 if highres timer available:
307 calculate highres time
308 cachedOffset = calibration.offset
309 highres time = calibration.last = max(highres time, calibration.last)
312 get kernel tick interval
314 if abs(highres - lowres) < kernel tick:
315 returnedTime = highres time
316 needCalibration = False
319 returnedTime = lowres
320 needCalibration = False
322 needCalibration = True
324 returnedTime = lowres
325 while needCalibration
333 JSInt64 s
, us
, ms2us
, s2us
;
337 static int nCalls
= 0;
338 long double lowresTime
, highresTimerValue
;
341 JSBool calibrated
= JS_FALSE
;
342 JSBool needsCalibration
= JS_FALSE
;
343 JSInt64 returnedTime
;
344 long double cachedOffset
= 0.0;
346 #if defined(XP_UNIX) || defined(XP_BEOS)
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
);
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 */
377 PR_CallOnce(&calibrationOnce
, NowInit
);
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);
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
,
432 &timeAdjustmentDisabled
)) {
433 if (timeAdjustmentDisabled
) {
434 /* timeAdjustment is in units of 100ns */
435 skewThreshold
= timeAdjustment
/10.0;
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);
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
;
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
475 needsCalibration
= JS_TRUE
;
478 /* No detectable clock skew */
479 returnedTime
= (JSInt64
)highresTime
;
480 needsCalibration
= JS_FALSE
;
483 /* No high resolution timer is available, so fall back */
484 returnedTime
= (JSInt64
)lowresTime
;
486 } while (needsCalibration
);
491 #if defined(XP_UNIX) || defined(XP_BEOS)
492 #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
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
);
506 /* Get the DST timezone offset for the time passed in */
508 PRMJ_DSTOffset(JSInt64 local_time
)
516 #ifndef HAVE_LOCALTIME_R
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
);
542 localtime_r(&local
,&tm
); /* get dst information */
545 diff
= ((tm
.tm_hour
- prtm
.tm_hour
) * PRMJ_HOUR_SECONDS
) +
546 ((tm
.tm_min
- prtm
.tm_min
) * 60);
549 diff
+= PRMJ_DAY_SECONDS
;
552 JSLL_UI2L(local_time
,diff
);
554 JSLL_MUL(local_time
,local_time
,us2s
);
559 /* Format a time value into a buffer. Same semantics as strftime() */
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)
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
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.
610 return strftime(buf
, buflen
, fmt
, &a
);
614 /* table for number of days in a month */
615 static int mtab
[] = {
616 /* jan, feb,mar,apr,may,jun */
618 /* july,aug,sep,oct,nov,dec */
623 * basic time calculation functionality for localtime and gmtime
624 * setups up prtm argument with correct values based upon input number
628 PRMJ_basetime(JSInt64 tsecs
, PRMJTime
*prtm
)
630 /* convert tsecs back to year,month,day,hour,secs */
635 JSInt32 wday
= 6; /* start on a Sunday */
642 /* Temporaries used for various computations */
649 /* Some variables for intermediate result storage to make computing isleap
651 JSInt32 fourCenturyBlocks
;
652 JSInt32 centuriesLeft
;
653 JSInt32 fourYearBlocksLeft
;
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. */
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
;
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
;
699 JSLL_UDIVMOD(&result1
, &result2
, tsecs
, century
);
700 JSLL_L2I(centuriesLeft
, result1
);
701 year
+= centuriesLeft
* 100;
702 days
+= centuriesLeft
* PRMJ_CENTURY_DAYS
;
705 JSLL_UDIVMOD(&result1
, &result2
, tsecs
, fourYears
);
706 JSLL_L2I(fourYearBlocksLeft
, result1
);
707 year
+= fourYearBlocksLeft
* 4;
708 days
+= fourYearBlocksLeft
* PRMJ_FOUR_YEARS_DAYS
;
711 /* Recall that |result| holds PRMJ_YEAR_SECONDS */
712 JSLL_UDIVMOD(&result1
, &result2
, tsecs
, result
);
713 JSLL_L2I(yearsLeft
, result1
);
715 days
+= yearsLeft
* PRMJ_YEAR_DAYS
;
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. */
722 (yearsLeft
== 3) && (fourYearBlocksLeft
!= 24 || centuriesLeft
== 3);
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
]))){
740 /* it's a Feb, check if this is a leap year */
741 if(month
== 1 && isleap
!= 0){
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 */
755 wday
= (days
+ wday
) % 7;
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
);
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
);
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
;