1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
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/
10 : *
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.
15 : *
16 : * The Original Code is Mozilla Communicator client code, released
17 : * March 31, 1998.
18 : *
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.
23 : *
24 : * Contributor(s):
25 : *
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.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : /*
41 : * PR time code.
42 : */
43 : #ifdef SOLARIS
44 : #define _REENTRANT 1
45 : #endif
46 : #include <string.h>
47 : #include <time.h>
48 :
49 : #include "jstypes.h"
50 : #include "jsutil.h"
51 :
52 : #include "jsprf.h"
53 : #include "jslock.h"
54 : #include "prmjtime.h"
55 :
56 : #define PRMJ_DO_MILLISECONDS 1
57 :
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 */
67 : #if _MSC_VER >= 1400
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
74 :
75 : #ifdef JS_THREADSAFE
76 : #include <prinit.h>
77 : #endif
78 :
79 : #endif
80 :
81 : #ifdef XP_UNIX
82 :
83 : #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
84 : extern int gettimeofday(struct timeval *tv);
85 : #endif
86 :
87 : #include <sys/time.h>
88 :
89 : #endif /* XP_UNIX */
90 :
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 */
99 :
100 : /* Get the local time. localtime_r is preferred as it is reentrant. */
101 : static inline bool
102 22342 : ComputeLocalTime(time_t local, struct tm *ptm)
103 : {
104 : #ifdef HAVE_LOCALTIME_R
105 22342 : return localtime_r(&local, ptm);
106 : #else
107 : struct tm *otm = localtime(&local);
108 : if (!otm)
109 : return false;
110 : *ptm = *otm;
111 : return true;
112 : #endif
113 : }
114 :
115 : /*
116 : * get the difference in seconds between this time zone and UTC (GMT)
117 : */
118 : int32_t
119 12360 : PRMJ_LocalGMTDifference()
120 : {
121 : #if defined(XP_WIN)
122 : /* Windows does not follow POSIX. Updates to the
123 : * TZ environment variable are not reflected
124 : * immediately on that platform as they are
125 : * on UNIX systems without this call.
126 : */
127 : _tzset();
128 : #endif
129 :
130 : /*
131 : * Get the difference between this time zone and GMT, by checking the local
132 : * time for days 0 and 180 of 1970, using a date for which daylight savings
133 : * time was not in effect.
134 : */
135 12360 : int day = 0;
136 : struct tm tm;
137 :
138 12360 : if (!ComputeLocalTime(0, &tm))
139 0 : return 0;
140 12360 : if (tm.tm_isdst > 0) {
141 0 : day = 180;
142 0 : if (!ComputeLocalTime(PRMJ_DAY_SECONDS * day, &tm))
143 0 : return 0;
144 : }
145 :
146 12360 : int time = (tm.tm_hour * 3600) + (tm.tm_min * 60) + tm.tm_sec;
147 12360 : time = PRMJ_DAY_SECONDS - time;
148 :
149 12360 : if (tm.tm_yday == day)
150 0 : time -= PRMJ_DAY_SECONDS;
151 :
152 12360 : return time;
153 : }
154 :
155 : /* Constants for GMT offset from 1970 */
156 : #define G1970GMTMICROHI 0x00dcdcad /* micro secs to 1970 hi */
157 : #define G1970GMTMICROLOW 0x8b3fa000 /* micro secs to 1970 low */
158 :
159 : #define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */
160 : #define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */
161 :
162 : #ifdef HAVE_SYSTEMTIMETOFILETIME
163 :
164 : static const int64_t win2un = 0x19DB1DED53E8000;
165 :
166 : #define FILETIME2INT64(ft) (((int64_t)ft.dwHighDateTime) << 32LL | (int64_t)ft.dwLowDateTime)
167 :
168 : #endif
169 :
170 : #if defined(HAVE_GETSYSTEMTIMEASFILETIME) || defined(HAVE_SYSTEMTIMETOFILETIME)
171 :
172 : #if defined(HAVE_GETSYSTEMTIMEASFILETIME)
173 : inline void
174 : LowResTime(LPFILETIME lpft)
175 : {
176 : GetSystemTimeAsFileTime(lpft);
177 : }
178 : #elif defined(HAVE_SYSTEMTIMETOFILETIME)
179 : inline void
180 : LowResTime(LPFILETIME lpft)
181 : {
182 : GetCurrentFT(lpft);
183 : }
184 : #else
185 : #error "No implementation of PRMJ_Now was selected."
186 : #endif
187 :
188 : typedef struct CalibrationData {
189 : long double freq; /* The performance counter frequency */
190 : long double offset; /* The low res 'epoch' */
191 : long double timer_offset; /* The high res 'epoch' */
192 :
193 : /* The last high res time that we returned since recalibrating */
194 : int64_t last;
195 :
196 : JSBool calibrated;
197 :
198 : #ifdef JS_THREADSAFE
199 : CRITICAL_SECTION data_lock;
200 : CRITICAL_SECTION calibration_lock;
201 : #endif
202 : } CalibrationData;
203 :
204 : static CalibrationData calibration = { 0 };
205 :
206 : static void
207 : NowCalibrate()
208 : {
209 : FILETIME ft, ftStart;
210 : LARGE_INTEGER liFreq, now;
211 :
212 : if (calibration.freq == 0.0) {
213 : if(!QueryPerformanceFrequency(&liFreq)) {
214 : /* High-performance timer is unavailable */
215 : calibration.freq = -1.0;
216 : } else {
217 : calibration.freq = (long double) liFreq.QuadPart;
218 : }
219 : }
220 : if (calibration.freq > 0.0) {
221 : int64_t calibrationDelta = 0;
222 :
223 : /* By wrapping a timeBegin/EndPeriod pair of calls around this loop,
224 : the loop seems to take much less time (1 ms vs 15ms) on Vista. */
225 : timeBeginPeriod(1);
226 : LowResTime(&ftStart);
227 : do {
228 : LowResTime(&ft);
229 : } while (memcmp(&ftStart,&ft, sizeof(ft)) == 0);
230 : timeEndPeriod(1);
231 :
232 : /*
233 : calibrationDelta = (FILETIME2INT64(ft) - FILETIME2INT64(ftStart))/10;
234 : fprintf(stderr, "Calibration delta was %I64d us\n", calibrationDelta);
235 : */
236 :
237 : QueryPerformanceCounter(&now);
238 :
239 : calibration.offset = (long double) FILETIME2INT64(ft);
240 : calibration.timer_offset = (long double) now.QuadPart;
241 :
242 : /* The windows epoch is around 1600. The unix epoch is around
243 : 1970. win2un is the difference (in windows time units which
244 : are 10 times more highres than the JS time unit) */
245 : calibration.offset -= win2un;
246 : calibration.offset *= 0.1;
247 : calibration.last = 0;
248 :
249 : calibration.calibrated = JS_TRUE;
250 : }
251 : }
252 :
253 : #define CALIBRATIONLOCK_SPINCOUNT 0
254 : #define DATALOCK_SPINCOUNT 4096
255 : #define LASTLOCK_SPINCOUNT 4096
256 :
257 : #ifdef JS_THREADSAFE
258 : static PRStatus
259 : NowInit(void)
260 : {
261 : memset(&calibration, 0, sizeof(calibration));
262 : NowCalibrate();
263 : InitializeCriticalSectionAndSpinCount(&calibration.calibration_lock, CALIBRATIONLOCK_SPINCOUNT);
264 : InitializeCriticalSectionAndSpinCount(&calibration.data_lock, DATALOCK_SPINCOUNT);
265 : return PR_SUCCESS;
266 : }
267 :
268 : void
269 : PRMJ_NowShutdown()
270 : {
271 : DeleteCriticalSection(&calibration.calibration_lock);
272 : DeleteCriticalSection(&calibration.data_lock);
273 : }
274 :
275 : #define MUTEX_LOCK(m) EnterCriticalSection(m)
276 : #define MUTEX_TRYLOCK(m) TryEnterCriticalSection(m)
277 : #define MUTEX_UNLOCK(m) LeaveCriticalSection(m)
278 : #define MUTEX_SETSPINCOUNT(m, c) SetCriticalSectionSpinCount((m),(c))
279 :
280 : static PRCallOnceType calibrationOnce = { 0 };
281 :
282 : #else
283 :
284 : #define MUTEX_LOCK(m)
285 : #define MUTEX_TRYLOCK(m) 1
286 : #define MUTEX_UNLOCK(m)
287 : #define MUTEX_SETSPINCOUNT(m, c)
288 :
289 : #endif
290 :
291 : #endif /* HAVE_GETSYSTEMTIMEASFILETIME */
292 :
293 :
294 : #if defined(XP_OS2)
295 : int64_t
296 : PRMJ_Now(void)
297 : {
298 : struct timeb b;
299 : ftime(&b);
300 : return (int64_t(b.time) * PRMJ_USEC_PER_SEC) + (int64_t(b.millitm) * PRMJ_USEC_PER_MSEC);
301 : }
302 :
303 : #elif defined(XP_UNIX)
304 : int64_t
305 10835474 : PRMJ_Now(void)
306 : {
307 : struct timeval tv;
308 :
309 : #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
310 : gettimeofday(&tv);
311 : #else
312 10835474 : gettimeofday(&tv, 0);
313 : #endif /* _SVID_GETTOD */
314 :
315 10835474 : return int64_t(tv.tv_sec) * PRMJ_USEC_PER_SEC + int64_t(tv.tv_usec);
316 : }
317 :
318 : #else
319 : /*
320 :
321 : Win32 python-esque pseudo code
322 : Please see bug 363258 for why the win32 timing code is so complex.
323 :
324 : calibration mutex : Win32CriticalSection(spincount=0)
325 : data mutex : Win32CriticalSection(spincount=4096)
326 :
327 : def NowInit():
328 : init mutexes
329 : PRMJ_NowCalibration()
330 :
331 : def NowCalibration():
332 : expensive up-to-15ms call
333 :
334 : def PRMJ_Now():
335 : returnedTime = 0
336 : needCalibration = False
337 : cachedOffset = 0.0
338 : calibrated = False
339 : PR_CallOnce(PRMJ_NowInit)
340 : do
341 : if not global.calibrated or needCalibration:
342 : acquire calibration mutex
343 : acquire data mutex
344 :
345 : // Only recalibrate if someone didn't already
346 : if cachedOffset == calibration.offset:
347 : // Have all waiting threads immediately wait
348 : set data mutex spin count = 0
349 : PRMJ_NowCalibrate()
350 : calibrated = 1
351 :
352 : set data mutex spin count = default
353 : release data mutex
354 : release calibration mutex
355 :
356 : calculate lowres time
357 :
358 : if highres timer available:
359 : acquire data mutex
360 : calculate highres time
361 : cachedOffset = calibration.offset
362 : highres time = calibration.last = max(highres time, calibration.last)
363 : release data mutex
364 :
365 : get kernel tick interval
366 :
367 : if abs(highres - lowres) < kernel tick:
368 : returnedTime = highres time
369 : needCalibration = False
370 : else:
371 : if calibrated:
372 : returnedTime = lowres
373 : needCalibration = False
374 : else:
375 : needCalibration = True
376 : else:
377 : returnedTime = lowres
378 : while needCalibration
379 :
380 : */
381 :
382 : // We parameterize the delay count just so that shell builds can
383 : // set it to 0 in order to get high-resolution benchmarking.
384 : // 10 seems to be the number of calls to load with a blank homepage.
385 : int CALIBRATION_DELAY_COUNT = 10;
386 :
387 : int64_t
388 : PRMJ_Now(void)
389 : {
390 : static int nCalls = 0;
391 : long double lowresTime, highresTimerValue;
392 : FILETIME ft;
393 : LARGE_INTEGER now;
394 : JSBool calibrated = JS_FALSE;
395 : JSBool needsCalibration = JS_FALSE;
396 : int64_t returnedTime;
397 : long double cachedOffset = 0.0;
398 :
399 : /* To avoid regressing startup time (where high resolution is likely
400 : not needed), give the old behavior for the first few calls.
401 : This does not appear to be needed on Vista as the timeBegin/timeEndPeriod
402 : calls seem to immediately take effect. */
403 : int thiscall = JS_ATOMIC_INCREMENT(&nCalls);
404 : if (thiscall <= CALIBRATION_DELAY_COUNT) {
405 : LowResTime(&ft);
406 : return (FILETIME2INT64(ft)-win2un)/10L;
407 : }
408 :
409 : /* For non threadsafe platforms, NowInit is not necessary */
410 : #ifdef JS_THREADSAFE
411 : PR_CallOnce(&calibrationOnce, NowInit);
412 : #endif
413 : do {
414 : if (!calibration.calibrated || needsCalibration) {
415 : MUTEX_LOCK(&calibration.calibration_lock);
416 : MUTEX_LOCK(&calibration.data_lock);
417 :
418 : /* Recalibrate only if no one else did before us */
419 : if(calibration.offset == cachedOffset) {
420 : /* Since calibration can take a while, make any other
421 : threads immediately wait */
422 : MUTEX_SETSPINCOUNT(&calibration.data_lock, 0);
423 :
424 : NowCalibrate();
425 :
426 : calibrated = JS_TRUE;
427 :
428 : /* Restore spin count */
429 : MUTEX_SETSPINCOUNT(&calibration.data_lock, DATALOCK_SPINCOUNT);
430 : }
431 : MUTEX_UNLOCK(&calibration.data_lock);
432 : MUTEX_UNLOCK(&calibration.calibration_lock);
433 : }
434 :
435 :
436 : /* Calculate a low resolution time */
437 : LowResTime(&ft);
438 : lowresTime = 0.1*(long double)(FILETIME2INT64(ft) - win2un);
439 :
440 : if (calibration.freq > 0.0) {
441 : long double highresTime, diff;
442 :
443 : DWORD timeAdjustment, timeIncrement;
444 : BOOL timeAdjustmentDisabled;
445 :
446 : /* Default to 15.625 ms if the syscall fails */
447 : long double skewThreshold = 15625.25;
448 : /* Grab high resolution time */
449 : QueryPerformanceCounter(&now);
450 : highresTimerValue = (long double)now.QuadPart;
451 :
452 : MUTEX_LOCK(&calibration.data_lock);
453 : highresTime = calibration.offset + PRMJ_USEC_PER_SEC*
454 : (highresTimerValue-calibration.timer_offset)/calibration.freq;
455 : cachedOffset = calibration.offset;
456 :
457 : /* On some dual processor/core systems, we might get an earlier time
458 : so we cache the last time that we returned */
459 : calibration.last = JS_MAX(calibration.last, int64_t(highresTime));
460 : returnedTime = calibration.last;
461 : MUTEX_UNLOCK(&calibration.data_lock);
462 :
463 : /* Rather than assume the NT kernel ticks every 15.6ms, ask it */
464 : if (GetSystemTimeAdjustment(&timeAdjustment,
465 : &timeIncrement,
466 : &timeAdjustmentDisabled)) {
467 : if (timeAdjustmentDisabled) {
468 : /* timeAdjustment is in units of 100ns */
469 : skewThreshold = timeAdjustment/10.0;
470 : } else {
471 : /* timeIncrement is in units of 100ns */
472 : skewThreshold = timeIncrement/10.0;
473 : }
474 : }
475 :
476 : /* Check for clock skew */
477 : diff = lowresTime - highresTime;
478 :
479 : /* For some reason that I have not determined, the skew can be
480 : up to twice a kernel tick. This does not seem to happen by
481 : itself, but I have only seen it triggered by another program
482 : doing some kind of file I/O. The symptoms are a negative diff
483 : followed by an equally large positive diff. */
484 : if (fabs(diff) > 2*skewThreshold) {
485 : /*fprintf(stderr,"Clock skew detected (diff = %f)!\n", diff);*/
486 :
487 : if (calibrated) {
488 : /* If we already calibrated once this instance, and the
489 : clock is still skewed, then either the processor(s) are
490 : wildly changing clockspeed or the system is so busy that
491 : we get switched out for long periods of time. In either
492 : case, it would be infeasible to make use of high
493 : resolution results for anything, so let's resort to old
494 : behavior for this call. It's possible that in the
495 : future, the user will want the high resolution timer, so
496 : we don't disable it entirely. */
497 : returnedTime = int64_t(lowresTime);
498 : needsCalibration = JS_FALSE;
499 : } else {
500 : /* It is possible that when we recalibrate, we will return a
501 : value less than what we have returned before; this is
502 : unavoidable. We cannot tell the different between a
503 : faulty QueryPerformanceCounter implementation and user
504 : changes to the operating system time. Since we must
505 : respect user changes to the operating system time, we
506 : cannot maintain the invariant that Date.now() never
507 : decreases; the old implementation has this behavior as
508 : well. */
509 : needsCalibration = JS_TRUE;
510 : }
511 : } else {
512 : /* No detectable clock skew */
513 : returnedTime = int64_t(highresTime);
514 : needsCalibration = JS_FALSE;
515 : }
516 : } else {
517 : /* No high resolution timer is available, so fall back */
518 : returnedTime = int64_t(lowresTime);
519 : }
520 : } while (needsCalibration);
521 :
522 : return returnedTime;
523 : }
524 : #endif
525 :
526 : #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
527 : static void
528 : PRMJ_InvalidParameterHandler(const wchar_t *expression,
529 : const wchar_t *function,
530 : const wchar_t *file,
531 : unsigned int line,
532 : uintptr_t pReserved)
533 : {
534 : /* empty */
535 : }
536 : #endif
537 :
538 : /* Format a time value into a buffer. Same semantics as strftime() */
539 : size_t
540 733 : PRMJ_FormatTime(char *buf, int buflen, const char *fmt, PRMJTime *prtm)
541 : {
542 733 : size_t result = 0;
543 : #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2)
544 : struct tm a;
545 733 : int fake_tm_year = 0;
546 : #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
547 : _invalid_parameter_handler oldHandler;
548 : int oldReportMode;
549 : #endif
550 :
551 733 : memset(&a, 0, sizeof(struct tm));
552 :
553 733 : a.tm_sec = prtm->tm_sec;
554 733 : a.tm_min = prtm->tm_min;
555 733 : a.tm_hour = prtm->tm_hour;
556 733 : a.tm_mday = prtm->tm_mday;
557 733 : a.tm_mon = prtm->tm_mon;
558 733 : a.tm_wday = prtm->tm_wday;
559 :
560 : /*
561 : * On systems where |struct tm| has members tm_gmtoff and tm_zone, we
562 : * must fill in those values, or else strftime will return wrong results
563 : * (e.g., bug 511726, bug 554338).
564 : */
565 : #if defined(HAVE_LOCALTIME_R) && defined(HAVE_TM_ZONE_TM_GMTOFF)
566 : {
567 : /*
568 : * Fill out |td| to the time represented by |prtm|, leaving the
569 : * timezone fields zeroed out. localtime_r will then fill in the
570 : * timezone fields for that local time according to the system's
571 : * timezone parameters.
572 : */
573 : struct tm td;
574 733 : memset(&td, 0, sizeof(td));
575 733 : td.tm_sec = prtm->tm_sec;
576 733 : td.tm_min = prtm->tm_min;
577 733 : td.tm_hour = prtm->tm_hour;
578 733 : td.tm_mday = prtm->tm_mday;
579 733 : td.tm_mon = prtm->tm_mon;
580 733 : td.tm_wday = prtm->tm_wday;
581 733 : td.tm_year = prtm->tm_year - 1900;
582 733 : td.tm_yday = prtm->tm_yday;
583 733 : td.tm_isdst = prtm->tm_isdst;
584 733 : time_t t = mktime(&td);
585 733 : localtime_r(&t, &td);
586 :
587 733 : a.tm_gmtoff = td.tm_gmtoff;
588 733 : a.tm_zone = td.tm_zone;
589 : }
590 : #endif
591 :
592 : /*
593 : * Years before 1900 and after 9999 cause strftime() to abort on Windows.
594 : * To avoid that we replace it with FAKE_YEAR_BASE + year % 100 and then
595 : * replace matching substrings in the strftime() result with the real year.
596 : * Note that FAKE_YEAR_BASE should be a multiple of 100 to make 2-digit
597 : * year formats (%y) work correctly (since we won't find the fake year
598 : * in that case).
599 : * e.g. new Date(1873, 0).toLocaleFormat('%Y %y') => "1873 73"
600 : * See bug 327869.
601 : */
602 : #define FAKE_YEAR_BASE 9900
603 733 : if (prtm->tm_year < 1900 || prtm->tm_year > 9999) {
604 0 : fake_tm_year = FAKE_YEAR_BASE + prtm->tm_year % 100;
605 0 : a.tm_year = fake_tm_year - 1900;
606 : }
607 : else {
608 733 : a.tm_year = prtm->tm_year - 1900;
609 : }
610 733 : a.tm_yday = prtm->tm_yday;
611 733 : a.tm_isdst = prtm->tm_isdst;
612 :
613 : /*
614 : * Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff
615 : * are null. This doesn't quite work, though - the timezone is off by
616 : * tzoff + dst. (And mktime seems to return -1 for the exact dst
617 : * changeover time.)
618 : */
619 :
620 : #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
621 : oldHandler = _set_invalid_parameter_handler(PRMJ_InvalidParameterHandler);
622 : oldReportMode = _CrtSetReportMode(_CRT_ASSERT, 0);
623 : #endif
624 :
625 733 : result = strftime(buf, buflen, fmt, &a);
626 :
627 : #ifdef NS_HAVE_INVALID_PARAMETER_HANDLER
628 : _set_invalid_parameter_handler(oldHandler);
629 : _CrtSetReportMode(_CRT_ASSERT, oldReportMode);
630 : #endif
631 :
632 733 : if (fake_tm_year && result) {
633 : char real_year[16];
634 : char fake_year[16];
635 : size_t real_year_len;
636 : size_t fake_year_len;
637 : char* p;
638 :
639 0 : sprintf(real_year, "%d", prtm->tm_year);
640 0 : real_year_len = strlen(real_year);
641 0 : sprintf(fake_year, "%d", fake_tm_year);
642 0 : fake_year_len = strlen(fake_year);
643 :
644 : /* Replace the fake year in the result with the real year. */
645 0 : for (p = buf; (p = strstr(p, fake_year)); p += real_year_len) {
646 0 : size_t new_result = result + real_year_len - fake_year_len;
647 0 : if ((int)new_result >= buflen) {
648 0 : return 0;
649 : }
650 0 : memmove(p + real_year_len, p + fake_year_len, strlen(p + fake_year_len));
651 0 : memcpy(p, real_year, real_year_len);
652 0 : result = new_result;
653 0 : *(buf + result) = '\0';
654 : }
655 : }
656 : #endif
657 733 : return result;
658 : }
659 :
660 : int64_t
661 9982 : DSTOffsetCache::computeDSTOffsetMilliseconds(int64_t localTimeSeconds)
662 : {
663 9982 : JS_ASSERT(localTimeSeconds >= 0);
664 9982 : JS_ASSERT(localTimeSeconds <= MAX_UNIX_TIMET);
665 :
666 : #if defined(XP_WIN)
667 : /* Windows does not follow POSIX. Updates to the
668 : * TZ environment variable are not reflected
669 : * immediately on that platform as they are
670 : * on UNIX systems without this call.
671 : */
672 : _tzset();
673 : #endif
674 :
675 : struct tm tm;
676 9982 : if (!ComputeLocalTime(static_cast<time_t>(localTimeSeconds), &tm))
677 0 : return 0;
678 :
679 9982 : int32_t base = PRMJ_LocalGMTDifference();
680 :
681 9982 : int32_t dayoff = int32_t((localTimeSeconds - base) % (SECONDS_PER_HOUR * 24));
682 : int32_t tmoff = tm.tm_sec + (tm.tm_min * SECONDS_PER_MINUTE) +
683 9982 : (tm.tm_hour * SECONDS_PER_HOUR);
684 :
685 9982 : int32_t diff = tmoff - dayoff;
686 :
687 9982 : if (diff < 0)
688 134 : diff += SECONDS_PER_DAY;
689 :
690 9982 : return diff * MILLISECONDS_PER_SECOND;
691 : }
692 :
693 : int64_t
694 55701 : DSTOffsetCache::getDSTOffsetMilliseconds(int64_t localTimeMilliseconds, JSContext *cx)
695 : {
696 55701 : sanityCheck();
697 :
698 55701 : int64_t localTimeSeconds = localTimeMilliseconds / MILLISECONDS_PER_SECOND;
699 :
700 55701 : if (localTimeSeconds > MAX_UNIX_TIMET) {
701 0 : localTimeSeconds = MAX_UNIX_TIMET;
702 55701 : } else if (localTimeSeconds < 0) {
703 : /* Go ahead a day to make localtime work (does not work with 0). */
704 0 : localTimeSeconds = SECONDS_PER_DAY;
705 : }
706 :
707 : /*
708 : * NB: Be aware of the initial range values when making changes to this
709 : * code: the first call to this method, with those initial range
710 : * values, must result in a cache miss.
711 : */
712 :
713 55701 : if (rangeStartSeconds <= localTimeSeconds &&
714 : localTimeSeconds <= rangeEndSeconds) {
715 44980 : return offsetMilliseconds;
716 : }
717 :
718 10721 : if (oldRangeStartSeconds <= localTimeSeconds &&
719 : localTimeSeconds <= oldRangeEndSeconds) {
720 4517 : return oldOffsetMilliseconds;
721 : }
722 :
723 6204 : oldOffsetMilliseconds = offsetMilliseconds;
724 6204 : oldRangeStartSeconds = rangeStartSeconds;
725 6204 : oldRangeEndSeconds = rangeEndSeconds;
726 :
727 6204 : if (rangeStartSeconds <= localTimeSeconds) {
728 5690 : int64_t newEndSeconds = JS_MIN(rangeEndSeconds + RANGE_EXPANSION_AMOUNT, MAX_UNIX_TIMET);
729 5690 : if (newEndSeconds >= localTimeSeconds) {
730 4964 : int64_t endOffsetMilliseconds = computeDSTOffsetMilliseconds(newEndSeconds);
731 4964 : if (endOffsetMilliseconds == offsetMilliseconds) {
732 1198 : rangeEndSeconds = newEndSeconds;
733 1198 : return offsetMilliseconds;
734 : }
735 :
736 3766 : offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds);
737 3766 : if (offsetMilliseconds == endOffsetMilliseconds) {
738 198 : rangeStartSeconds = localTimeSeconds;
739 198 : rangeEndSeconds = newEndSeconds;
740 : } else {
741 3568 : rangeEndSeconds = localTimeSeconds;
742 : }
743 3766 : return offsetMilliseconds;
744 : }
745 :
746 726 : offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds);
747 726 : rangeStartSeconds = rangeEndSeconds = localTimeSeconds;
748 726 : return offsetMilliseconds;
749 : }
750 :
751 514 : int64_t newStartSeconds = JS_MAX(rangeStartSeconds - RANGE_EXPANSION_AMOUNT, 0);
752 514 : if (newStartSeconds <= localTimeSeconds) {
753 149 : int64_t startOffsetMilliseconds = computeDSTOffsetMilliseconds(newStartSeconds);
754 149 : if (startOffsetMilliseconds == offsetMilliseconds) {
755 137 : rangeStartSeconds = newStartSeconds;
756 137 : return offsetMilliseconds;
757 : }
758 :
759 12 : offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds);
760 12 : if (offsetMilliseconds == startOffsetMilliseconds) {
761 0 : rangeStartSeconds = newStartSeconds;
762 0 : rangeEndSeconds = localTimeSeconds;
763 : } else {
764 12 : rangeStartSeconds = localTimeSeconds;
765 : }
766 12 : return offsetMilliseconds;
767 : }
768 :
769 365 : rangeStartSeconds = rangeEndSeconds = localTimeSeconds;
770 365 : offsetMilliseconds = computeDSTOffsetMilliseconds(localTimeSeconds);
771 365 : return offsetMilliseconds;
772 : }
773 :
774 : void
775 81932 : DSTOffsetCache::sanityCheck()
776 : {
777 81932 : JS_ASSERT(rangeStartSeconds <= rangeEndSeconds);
778 81932 : JS_ASSERT_IF(rangeStartSeconds == INT64_MIN, rangeEndSeconds == INT64_MIN);
779 81932 : JS_ASSERT_IF(rangeEndSeconds == INT64_MIN, rangeStartSeconds == INT64_MIN);
780 0 : JS_ASSERT_IF(rangeStartSeconds != INT64_MIN,
781 81932 : rangeStartSeconds >= 0 && rangeEndSeconds >= 0);
782 0 : JS_ASSERT_IF(rangeStartSeconds != INT64_MIN,
783 81932 : rangeStartSeconds <= MAX_UNIX_TIMET && rangeEndSeconds <= MAX_UNIX_TIMET);
784 81932 : }
|