Bug Summary

File:pr/Linux4.19_x86_64_gcc_glibc_PTH_64_DBG.OBJ/pr/src/misc/../../../../pr/src/misc/prtime.c
Warning:line 1669, column 9
Value stored to 'zone' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name prtime.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/nss-scan-build/nspr/Linux4.19_x86_64_gcc_glibc_PTH_64_DBG.OBJ/pr/src/misc -fcoverage-compilation-dir=/var/lib/jenkins/workspace/nss-scan-build/nspr/Linux4.19_x86_64_gcc_glibc_PTH_64_DBG.OBJ/pr/src/misc -resource-dir /usr/lib/llvm-18/lib/clang/18 -U NDEBUG -D DEBUG_jenkins -D PACKAGE_NAME="" -D PACKAGE_TARNAME="" -D PACKAGE_VERSION="" -D PACKAGE_STRING="" -D PACKAGE_BUGREPORT="" -D PACKAGE_URL="" -D DEBUG=1 -D HAVE_VISIBILITY_HIDDEN_ATTRIBUTE=1 -D HAVE_VISIBILITY_PRAGMA=1 -D XP_UNIX=1 -D _GNU_SOURCE=1 -D HAVE_FCNTL_FILE_LOCKING=1 -D HAVE_POINTER_LOCALTIME_R=1 -D LINUX=1 -D HAVE_DLADDR=1 -D HAVE_GETTID=1 -D HAVE_LCHOWN=1 -D HAVE_SETPRIORITY=1 -D HAVE_STRERROR=1 -D HAVE_SYSCALL=1 -D HAVE_SECURE_GETENV=1 -D _REENTRANT=1 -D FORCE_PR_LOG -D _PR_PTHREADS -U HAVE_CVAR_BUILT_ON_SEM -D _NSPR_BUILD_ -I /var/lib/jenkins/workspace/nss-scan-build/nss/../dist/Linux4.19_x86_64_gcc_glibc_PTH_64_DBG.OBJ/include -I ../../../../pr/include -I ../../../../pr/include/private -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -ferror-limit 19 -fvisibility=hidden -fgnuc-version=4.2.1 -fno-inline -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2024-05-18-082241-28900-1 -x c ../../../../pr/src/misc/prtime.c
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6/*
7 * prtime.c --
8 *
9 * NSPR date and time functions
10 *
11 */
12
13#include "prinit.h"
14#include "prtime.h"
15#include "prlock.h"
16#include "prprf.h"
17#include "prlog.h"
18
19#include <string.h>
20#include <ctype.h>
21#include <errno(*__errno_location ()).h> /* for EINVAL */
22#include <time.h>
23
24/*
25 * The COUNT_LEAPS macro counts the number of leap years passed by
26 * till the start of the given year Y. At the start of the year 4
27 * A.D. the number of leap years passed by is 0, while at the start of
28 * the year 5 A.D. this count is 1. The number of years divisible by
29 * 100 but not divisible by 400 (the non-leap years) is deducted from
30 * the count to get the correct number of leap years.
31 *
32 * The COUNT_DAYS macro counts the number of days since 01/01/01 till the
33 * start of the given year Y. The number of days at the start of the year
34 * 1 is 0 while the number of days at the start of the year 2 is 365
35 * (which is ((2)-1) * 365) and so on. The reference point is 01/01/01
36 * midnight 00:00:00.
37 */
38
39#define COUNT_LEAPS(Y)( ((Y)-1)/4 - ((Y)-1)/100 + ((Y)-1)/400 ) ( ((Y)-1)/4 - ((Y)-1)/100 + ((Y)-1)/400 )
40#define COUNT_DAYS(Y)( ((Y)-1)*365 + ( ((Y)-1)/4 - ((Y)-1)/100 + ((Y)-1)/400 ) ) ( ((Y)-1)*365 + COUNT_LEAPS(Y)( ((Y)-1)/4 - ((Y)-1)/100 + ((Y)-1)/400 ) )
41#define DAYS_BETWEEN_YEARS(A, B)(( ((B)-1)*365 + ( ((B)-1)/4 - ((B)-1)/100 + ((B)-1)/400 ) ) -
( ((A)-1)*365 + ( ((A)-1)/4 - ((A)-1)/100 + ((A)-1)/400 ) ))
(COUNT_DAYS(B)( ((B)-1)*365 + ( ((B)-1)/4 - ((B)-1)/100 + ((B)-1)/400 ) ) - COUNT_DAYS(A)( ((A)-1)*365 + ( ((A)-1)/4 - ((A)-1)/100 + ((A)-1)/400 ) ))
42
43/*
44 * Static variables used by functions in this file
45 */
46
47/*
48 * The following array contains the day of year for the last day of
49 * each month, where index 1 is January, and day 0 is January 1.
50 */
51
52static const int lastDayOfMonth[2][13] = {
53 {-1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364},
54 {-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}
55};
56
57/*
58 * The number of days in a month
59 */
60
61static const PRInt8 nDays[2][12] = {
62 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
63 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
64};
65
66/*
67 * Declarations for internal functions defined later in this file.
68 */
69
70static void ComputeGMT(PRTime time, PRExplodedTime *gmt);
71static int IsLeapYear(PRInt16 year);
72static void ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset);
73
74/*
75 *------------------------------------------------------------------------
76 *
77 * ComputeGMT --
78 *
79 * Caveats:
80 * - we ignore leap seconds
81 *
82 *------------------------------------------------------------------------
83 */
84
85static void
86ComputeGMT(PRTime time, PRExplodedTime *gmt)
87{
88 PRInt32 tmp, rem;
89 PRInt32 numDays;
90 PRInt64 numDays64, rem64;
91 int isLeap;
92 PRInt64 sec;
93 PRInt64 usec;
94 PRInt64 usecPerSec;
95 PRInt64 secPerDay;
96
97 /*
98 * We first do the usec, sec, min, hour thing so that we do not
99 * have to do LL arithmetic.
100 */
101
102 LL_I2L(usecPerSec, 1000000L)((usecPerSec) = (PRInt64)(1000000L));
103 LL_DIV(sec, time, usecPerSec)((sec) = (time) / (usecPerSec));
104 LL_MOD(usec, time, usecPerSec)((usec) = (time) % (usecPerSec));
105 LL_L2I(gmt->tm_usec, usec)((gmt->tm_usec) = (PRInt32)(usec));
106 /* Correct for weird mod semantics so the remainder is always positive */
107 if (gmt->tm_usec < 0) {
108 PRInt64 one;
109
110 LL_I2L(one, 1L)((one) = (PRInt64)(1L));
111 LL_SUB(sec, sec, one)((sec) = (sec) - (one));
112 gmt->tm_usec += 1000000L;
113 }
114
115 LL_I2L(secPerDay, 86400L)((secPerDay) = (PRInt64)(86400L));
116 LL_DIV(numDays64, sec, secPerDay)((numDays64) = (sec) / (secPerDay));
117 LL_MOD(rem64, sec, secPerDay)((rem64) = (sec) % (secPerDay));
118 /* We are sure both of these numbers can fit into PRInt32 */
119 LL_L2I(numDays, numDays64)((numDays) = (PRInt32)(numDays64));
120 LL_L2I(rem, rem64)((rem) = (PRInt32)(rem64));
121 if (rem < 0) {
122 numDays--;
123 rem += 86400L;
124 }
125
126 /* Compute day of week. Epoch started on a Thursday. */
127
128 gmt->tm_wday = (numDays + 4) % 7;
129 if (gmt->tm_wday < 0) {
130 gmt->tm_wday += 7;
131 }
132
133 /* Compute the time of day. */
134
135 gmt->tm_hour = rem / 3600;
136 rem %= 3600;
137 gmt->tm_min = rem / 60;
138 gmt->tm_sec = rem % 60;
139
140 /*
141 * Compute the year by finding the 400 year period, then working
142 * down from there.
143 *
144 * Since numDays is originally the number of days since January 1, 1970,
145 * we must change it to be the number of days from January 1, 0001.
146 */
147
148 numDays += 719162; /* 719162 = days from year 1 up to 1970 */
149 tmp = numDays / 146097; /* 146097 = days in 400 years */
150 rem = numDays % 146097;
151 gmt->tm_year = tmp * 400 + 1;
152
153 /* Compute the 100 year period. */
154
155 tmp = rem / 36524; /* 36524 = days in 100 years */
156 rem %= 36524;
157 if (tmp == 4) { /* the 400th year is a leap year */
158 tmp = 3;
159 rem = 36524;
160 }
161 gmt->tm_year += tmp * 100;
162
163 /* Compute the 4 year period. */
164
165 tmp = rem / 1461; /* 1461 = days in 4 years */
166 rem %= 1461;
167 gmt->tm_year += tmp * 4;
168
169 /* Compute which year in the 4. */
170
171 tmp = rem / 365;
172 rem %= 365;
173 if (tmp == 4) { /* the 4th year is a leap year */
174 tmp = 3;
175 rem = 365;
176 }
177
178 gmt->tm_year += tmp;
179 gmt->tm_yday = rem;
180 isLeap = IsLeapYear(gmt->tm_year);
181
182 /* Compute the month and day of month. */
183
184 for (tmp = 1; lastDayOfMonth[isLeap][tmp] < gmt->tm_yday; tmp++) {
185 }
186 gmt->tm_month = --tmp;
187 gmt->tm_mday = gmt->tm_yday - lastDayOfMonth[isLeap][tmp];
188
189 gmt->tm_params.tp_gmt_offset = 0;
190 gmt->tm_params.tp_dst_offset = 0;
191}
192
193
194/*
195 *------------------------------------------------------------------------
196 *
197 * PR_ExplodeTime --
198 *
199 * Cf. struct tm *gmtime(const time_t *tp) and
200 * struct tm *localtime(const time_t *tp)
201 *
202 *------------------------------------------------------------------------
203 */
204
205PR_IMPLEMENT(void)__attribute__((visibility("default"))) void
206PR_ExplodeTime(
207 PRTime usecs,
208 PRTimeParamFn params,
209 PRExplodedTime *exploded)
210{
211 ComputeGMT(usecs, exploded);
212 exploded->tm_params = params(exploded);
213 ApplySecOffset(exploded, exploded->tm_params.tp_gmt_offset
214 + exploded->tm_params.tp_dst_offset);
215}
216
217
218/*
219 *------------------------------------------------------------------------
220 *
221 * PR_ImplodeTime --
222 *
223 * Cf. time_t mktime(struct tm *tp)
224 * Note that 1 year has < 2^25 seconds. So an PRInt32 is large enough.
225 *
226 *------------------------------------------------------------------------
227 */
228PR_IMPLEMENT(PRTime)__attribute__((visibility("default"))) PRTime
229PR_ImplodeTime(const PRExplodedTime *exploded)
230{
231 PRExplodedTime copy;
232 PRTime retVal;
233 PRInt64 secPerDay, usecPerSec;
234 PRInt64 temp;
235 PRInt64 numSecs64;
236 PRInt32 numDays;
237 PRInt32 numSecs;
238
239 /* Normalize first. Do this on our copy */
240 copy = *exploded;
241 PR_NormalizeTime(&copy, PR_GMTParameters);
242
243 numDays = DAYS_BETWEEN_YEARS(1970, copy.tm_year)(( ((copy.tm_year)-1)*365 + ( ((copy.tm_year)-1)/4 - ((copy.tm_year
)-1)/100 + ((copy.tm_year)-1)/400 ) ) - ( ((1970)-1)*365 + ( (
(1970)-1)/4 - ((1970)-1)/100 + ((1970)-1)/400 ) ))
;
244
245 numSecs = copy.tm_yday * 86400 + copy.tm_hour * 3600
246 + copy.tm_min * 60 + copy.tm_sec;
247
248 LL_I2L(temp, numDays)((temp) = (PRInt64)(numDays));
249 LL_I2L(secPerDay, 86400)((secPerDay) = (PRInt64)(86400));
250 LL_MUL(temp, temp, secPerDay)((temp) = (temp) * (secPerDay));
251 LL_I2L(numSecs64, numSecs)((numSecs64) = (PRInt64)(numSecs));
252 LL_ADD(numSecs64, numSecs64, temp)((numSecs64) = (numSecs64) + (temp));
253
254 /* apply the GMT and DST offsets */
255 LL_I2L(temp, copy.tm_params.tp_gmt_offset)((temp) = (PRInt64)(copy.tm_params.tp_gmt_offset));
256 LL_SUB(numSecs64, numSecs64, temp)((numSecs64) = (numSecs64) - (temp));
257 LL_I2L(temp, copy.tm_params.tp_dst_offset)((temp) = (PRInt64)(copy.tm_params.tp_dst_offset));
258 LL_SUB(numSecs64, numSecs64, temp)((numSecs64) = (numSecs64) - (temp));
259
260 LL_I2L(usecPerSec, 1000000L)((usecPerSec) = (PRInt64)(1000000L));
261 LL_MUL(temp, numSecs64, usecPerSec)((temp) = (numSecs64) * (usecPerSec));
262 LL_I2L(retVal, copy.tm_usec)((retVal) = (PRInt64)(copy.tm_usec));
263 LL_ADD(retVal, retVal, temp)((retVal) = (retVal) + (temp));
264
265 return retVal;
266}
267
268/*
269 *-------------------------------------------------------------------------
270 *
271 * IsLeapYear --
272 *
273 * Returns 1 if the year is a leap year, 0 otherwise.
274 *
275 *-------------------------------------------------------------------------
276 */
277
278static int IsLeapYear(PRInt16 year)
279{
280 if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
281 return 1;
282 }
283 return 0;
284}
285
286/*
287 * 'secOffset' should be less than 86400 (i.e., a day).
288 * 'time' should point to a normalized PRExplodedTime.
289 */
290
291static void
292ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset)
293{
294 time->tm_sec += secOffset;
295
296 /* Note that in this implementation we do not count leap seconds */
297 if (time->tm_sec < 0 || time->tm_sec >= 60) {
298 time->tm_min += time->tm_sec / 60;
299 time->tm_sec %= 60;
300 if (time->tm_sec < 0) {
301 time->tm_sec += 60;
302 time->tm_min--;
303 }
304 }
305
306 if (time->tm_min < 0 || time->tm_min >= 60) {
307 time->tm_hour += time->tm_min / 60;
308 time->tm_min %= 60;
309 if (time->tm_min < 0) {
310 time->tm_min += 60;
311 time->tm_hour--;
312 }
313 }
314
315 if (time->tm_hour < 0) {
316 /* Decrement mday, yday, and wday */
317 time->tm_hour += 24;
318 time->tm_mday--;
319 time->tm_yday--;
320 if (time->tm_mday < 1) {
321 time->tm_month--;
322 if (time->tm_month < 0) {
323 time->tm_month = 11;
324 time->tm_year--;
325 if (IsLeapYear(time->tm_year)) {
326 time->tm_yday = 365;
327 }
328 else {
329 time->tm_yday = 364;
330 }
331 }
332 time->tm_mday = nDays[IsLeapYear(time->tm_year)][time->tm_month];
333 }
334 time->tm_wday--;
335 if (time->tm_wday < 0) {
336 time->tm_wday = 6;
337 }
338 } else if (time->tm_hour > 23) {
339 /* Increment mday, yday, and wday */
340 time->tm_hour -= 24;
341 time->tm_mday++;
342 time->tm_yday++;
343 if (time->tm_mday >
344 nDays[IsLeapYear(time->tm_year)][time->tm_month]) {
345 time->tm_mday = 1;
346 time->tm_month++;
347 if (time->tm_month > 11) {
348 time->tm_month = 0;
349 time->tm_year++;
350 time->tm_yday = 0;
351 }
352 }
353 time->tm_wday++;
354 if (time->tm_wday > 6) {
355 time->tm_wday = 0;
356 }
357 }
358}
359
360PR_IMPLEMENT(void)__attribute__((visibility("default"))) void
361PR_NormalizeTime(PRExplodedTime *time, PRTimeParamFn params)
362{
363 int daysInMonth;
364 PRInt32 numDays;
365
366 /* Get back to GMT */
367 time->tm_sec -= time->tm_params.tp_gmt_offset
368 + time->tm_params.tp_dst_offset;
369 time->tm_params.tp_gmt_offset = 0;
370 time->tm_params.tp_dst_offset = 0;
371
372 /* Now normalize GMT */
373
374 if (time->tm_usec < 0 || time->tm_usec >= 1000000) {
375 time->tm_sec += time->tm_usec / 1000000;
376 time->tm_usec %= 1000000;
377 if (time->tm_usec < 0) {
378 time->tm_usec += 1000000;
379 time->tm_sec--;
380 }
381 }
382
383 /* Note that we do not count leap seconds in this implementation */
384 if (time->tm_sec < 0 || time->tm_sec >= 60) {
385 time->tm_min += time->tm_sec / 60;
386 time->tm_sec %= 60;
387 if (time->tm_sec < 0) {
388 time->tm_sec += 60;
389 time->tm_min--;
390 }
391 }
392
393 if (time->tm_min < 0 || time->tm_min >= 60) {
394 time->tm_hour += time->tm_min / 60;
395 time->tm_min %= 60;
396 if (time->tm_min < 0) {
397 time->tm_min += 60;
398 time->tm_hour--;
399 }
400 }
401
402 if (time->tm_hour < 0 || time->tm_hour >= 24) {
403 time->tm_mday += time->tm_hour / 24;
404 time->tm_hour %= 24;
405 if (time->tm_hour < 0) {
406 time->tm_hour += 24;
407 time->tm_mday--;
408 }
409 }
410
411 /* Normalize month and year before mday */
412 if (time->tm_month < 0 || time->tm_month >= 12) {
413 time->tm_year += time->tm_month / 12;
414 time->tm_month %= 12;
415 if (time->tm_month < 0) {
416 time->tm_month += 12;
417 time->tm_year--;
418 }
419 }
420
421 /* Now that month and year are in proper range, normalize mday */
422
423 if (time->tm_mday < 1) {
424 /* mday too small */
425 do {
426 /* the previous month */
427 time->tm_month--;
428 if (time->tm_month < 0) {
429 time->tm_month = 11;
430 time->tm_year--;
431 }
432 time->tm_mday += nDays[IsLeapYear(time->tm_year)][time->tm_month];
433 } while (time->tm_mday < 1);
434 } else {
435 daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month];
436 while (time->tm_mday > daysInMonth) {
437 /* mday too large */
438 time->tm_mday -= daysInMonth;
439 time->tm_month++;
440 if (time->tm_month > 11) {
441 time->tm_month = 0;
442 time->tm_year++;
443 }
444 daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month];
445 }
446 }
447
448 /* Recompute yday and wday */
449 time->tm_yday = time->tm_mday +
450 lastDayOfMonth[IsLeapYear(time->tm_year)][time->tm_month];
451
452 numDays = DAYS_BETWEEN_YEARS(1970, time->tm_year)(( ((time->tm_year)-1)*365 + ( ((time->tm_year)-1)/4 - (
(time->tm_year)-1)/100 + ((time->tm_year)-1)/400 ) ) - (
((1970)-1)*365 + ( ((1970)-1)/4 - ((1970)-1)/100 + ((1970)-1
)/400 ) ))
+ time->tm_yday;
453 time->tm_wday = (numDays + 4) % 7;
454 if (time->tm_wday < 0) {
455 time->tm_wday += 7;
456 }
457
458 /* Recompute time parameters */
459
460 time->tm_params = params(time);
461
462 ApplySecOffset(time, time->tm_params.tp_gmt_offset
463 + time->tm_params.tp_dst_offset);
464}
465
466
467/*
468 *-------------------------------------------------------------------------
469 *
470 * PR_LocalTimeParameters --
471 *
472 * returns the time parameters for the local time zone
473 *
474 * The following uses localtime() from the standard C library.
475 * (time.h) This is our fallback implementation. Unix, PC, and BeOS
476 * use this version. A platform may have its own machine-dependent
477 * implementation of this function.
478 *
479 *-------------------------------------------------------------------------
480 */
481
482#if defined(HAVE_INT_LOCALTIME_R)
483
484/*
485 * In this case we could define the macro as
486 * #define MT_safe_localtime(timer, result) \
487 * (localtime_r(timer, result) == 0 ? result : NULL)
488 * I chose to compare the return value of localtime_r with -1 so
489 * that I can catch the cases where localtime_r returns a pointer
490 * to struct tm. The macro definition above would not be able to
491 * detect such mistakes because it is legal to compare a pointer
492 * with 0.
493 */
494
495#define MT_safe_localtimelocaltime_r(timer, result) \
496 (localtime_r(timer, result) == -1 ? NULL((void*)0): result)
497
498#elif defined(HAVE_POINTER_LOCALTIME_R1)
499
500#define MT_safe_localtimelocaltime_r localtime_r
501
502#elif defined(_MSC_VER)
503
504/* Visual C++ has had localtime_s() since Visual C++ 2005. */
505
506static struct tm *MT_safe_localtimelocaltime_r(const time_t *clock, struct tm *result)
507{
508 errno_t err = localtime_s(result, clock);
509 if (err != 0) {
510 errno(*__errno_location ()) = err;
511 return NULL((void*)0);
512 }
513 return result;
514}
515
516#else
517
518#define HAVE_LOCALTIME_MONITOR 1 /* We use 'monitor' to serialize our calls
519 * to localtime(). */
520static PRLock *monitor = NULL((void*)0);
521
522static struct tm *MT_safe_localtimelocaltime_r(const time_t *clock, struct tm *result)
523{
524 struct tm *tmPtr;
525 int needLock = PR_Initialized(); /* We need to use a lock to protect
526 * against NSPR threads only when the
527 * NSPR thread system is activated. */
528
529 if (needLock) {
530 PR_Lock(monitor);
531 }
532
533 /*
534 * Microsoft (all flavors) localtime() returns a NULL pointer if 'clock'
535 * represents a time before midnight January 1, 1970. In
536 * that case, we also return a NULL pointer and the struct tm
537 * object pointed to by 'result' is not modified.
538 *
539 * Watcom C/C++ 11.0 localtime() treats time_t as unsigned long
540 * hence, does not recognize negative values of clock as pre-1/1/70.
541 * We have to manually check (WIN16 only) for negative value of
542 * clock and return NULL.
543 *
544 * With negative values of clock, OS/2 returns the struct tm for
545 * clock plus ULONG_MAX. So we also have to check for the invalid
546 * structs returned for timezones west of Greenwich when clock == 0.
547 */
548
549 tmPtr = localtime(clock);
550
551#if defined(WIN16) || defined(XP_OS2)
552 if ( (PRInt32) *clock < 0 ||
553 ( (PRInt32) *clock == 0 && tmPtr->tm_year != 70)) {
554 result = NULL((void*)0);
555 }
556 else {
557 *result = *tmPtr;
558 }
559#else
560 if (tmPtr) {
561 *result = *tmPtr;
562 } else {
563 result = NULL((void*)0);
564 }
565#endif /* WIN16 */
566
567 if (needLock) {
568 PR_Unlock(monitor);
569 }
570
571 return result;
572}
573
574#endif /* definition of MT_safe_localtime() */
575
576void _PR_InitTime(void)
577{
578#ifdef HAVE_LOCALTIME_MONITOR
579 monitor = PR_NewLock();
580#endif
581#ifdef WINCE
582 _MD_InitTime();
583#endif
584}
585
586void _PR_CleanupTime(void)
587{
588#ifdef HAVE_LOCALTIME_MONITOR
589 if (monitor) {
590 PR_DestroyLock(monitor);
591 monitor = NULL((void*)0);
592 }
593#endif
594#ifdef WINCE
595 _MD_CleanupTime();
596#endif
597}
598
599#if defined(XP_UNIX1) || defined(XP_PC)
600
601PR_IMPLEMENT(PRTimeParameters)__attribute__((visibility("default"))) PRTimeParameters
602PR_LocalTimeParameters(const PRExplodedTime *gmt)
603{
604
605 PRTimeParameters retVal;
606 struct tm localTime;
607 struct tm *localTimeResult;
608 time_t secs;
609 PRTime secs64;
610 PRInt64 usecPerSec;
611 PRInt64 usecPerSec_1;
612 PRInt64 maxInt32;
613 PRInt64 minInt32;
614 PRInt32 dayOffset;
615 PRInt32 offset2Jan1970;
616 PRInt32 offsetNew;
617 int isdst2Jan1970;
618
619 /*
620 * Calculate the GMT offset. First, figure out what is
621 * 00:00:00 Jan. 2, 1970 GMT (which is exactly a day, or 86400
622 * seconds, since the epoch) in local time. Then we calculate
623 * the difference between local time and GMT in seconds:
624 * gmt_offset = local_time - GMT
625 *
626 * Caveat: the validity of this calculation depends on two
627 * assumptions:
628 * 1. Daylight saving time was not in effect on Jan. 2, 1970.
629 * 2. The time zone of the geographic location has not changed
630 * since Jan. 2, 1970.
631 */
632
633 secs = 86400L;
634 localTimeResult = MT_safe_localtimelocaltime_r(&secs, &localTime);
635 PR_ASSERT(localTimeResult != NULL)((localTimeResult != ((void*)0))?((void)0):PR_Assert("localTimeResult != NULL"
,"../../../../pr/src/misc/prtime.c",635))
;
636 if (localTimeResult == NULL((void*)0)) {
637 /* Shouldn't happen. Use safe fallback for optimized builds. */
638 return PR_GMTParameters(gmt);
639 }
640
641 /* GMT is 00:00:00, 2nd of Jan. */
642
643 offset2Jan1970 = (PRInt32)localTime.tm_sec
644 + 60L * (PRInt32)localTime.tm_min
645 + 3600L * (PRInt32)localTime.tm_hour
646 + 86400L * (PRInt32)((PRInt32)localTime.tm_mday - 2L);
647
648 isdst2Jan1970 = localTime.tm_isdst;
649
650 /*
651 * Now compute DST offset. We calculate the overall offset
652 * of local time from GMT, similar to above. The overall
653 * offset has two components: gmt offset and dst offset.
654 * We subtract gmt offset from the overall offset to get
655 * the dst offset.
656 * overall_offset = local_time - GMT
657 * overall_offset = gmt_offset + dst_offset
658 * ==> dst_offset = local_time - GMT - gmt_offset
659 */
660
661 secs64 = PR_ImplodeTime(gmt); /* This is still in microseconds */
662 LL_I2L(usecPerSec, PR_USEC_PER_SEC)((usecPerSec) = (PRInt64)(1000000L));
663 LL_I2L(usecPerSec_1, PR_USEC_PER_SEC - 1)((usecPerSec_1) = (PRInt64)(1000000L - 1));
664 /* Convert to seconds, truncating down (3.1 -> 3 and -3.1 -> -4) */
665 if (LL_GE_ZERO(secs64)((secs64) >= 0)) {
666 LL_DIV(secs64, secs64, usecPerSec)((secs64) = (secs64) / (usecPerSec));
667 } else {
668 LL_NEG(secs64, secs64)((secs64) = -(secs64));
669 LL_ADD(secs64, secs64, usecPerSec_1)((secs64) = (secs64) + (usecPerSec_1));
670 LL_DIV(secs64, secs64, usecPerSec)((secs64) = (secs64) / (usecPerSec));
671 LL_NEG(secs64, secs64)((secs64) = -(secs64));
672 }
673 LL_I2L(maxInt32, PR_INT32_MAX)((maxInt32) = (PRInt64)(2147483647));
674 LL_I2L(minInt32, PR_INT32_MIN)((minInt32) = (PRInt64)((-2147483647 - 1)));
675 if (LL_CMP(secs64, >, maxInt32)((PRInt64)(secs64) > (PRInt64)(maxInt32)) || LL_CMP(secs64, <, minInt32)((PRInt64)(secs64) < (PRInt64)(minInt32))) {
676 /* secs64 is too large or too small for time_t (32-bit integer) */
677 retVal.tp_gmt_offset = offset2Jan1970;
678 retVal.tp_dst_offset = 0;
679 return retVal;
680 }
681 LL_L2I(secs, secs64)((secs) = (PRInt32)(secs64));
682
683 /*
684 * On Windows, localtime() (and our MT_safe_localtime() too)
685 * returns a NULL pointer for time before midnight January 1,
686 * 1970 GMT. In that case, we just use the GMT offset for
687 * Jan 2, 1970 and assume that DST was not in effect.
688 */
689
690 if (MT_safe_localtimelocaltime_r(&secs, &localTime) == NULL((void*)0)) {
691 retVal.tp_gmt_offset = offset2Jan1970;
692 retVal.tp_dst_offset = 0;
693 return retVal;
694 }
695
696 /*
697 * dayOffset is the offset between local time and GMT in
698 * the day component, which can only be -1, 0, or 1. We
699 * use the day of the week to compute dayOffset.
700 */
701
702 dayOffset = (PRInt32) localTime.tm_wday - gmt->tm_wday;
703
704 /*
705 * Need to adjust for wrapping around of day of the week from
706 * 6 back to 0.
707 */
708
709 if (dayOffset == -6) {
710 /* Local time is Sunday (0) and GMT is Saturday (6) */
711 dayOffset = 1;
712 } else if (dayOffset == 6) {
713 /* Local time is Saturday (6) and GMT is Sunday (0) */
714 dayOffset = -1;
715 }
716
717 offsetNew = (PRInt32)localTime.tm_sec - gmt->tm_sec
718 + 60L * ((PRInt32)localTime.tm_min - gmt->tm_min)
719 + 3600L * ((PRInt32)localTime.tm_hour - gmt->tm_hour)
720 + 86400L * (PRInt32)dayOffset;
721
722 if (localTime.tm_isdst <= 0) {
723 /* DST is not in effect */
724 retVal.tp_gmt_offset = offsetNew;
725 retVal.tp_dst_offset = 0;
726 } else {
727 /* DST is in effect */
728 if (isdst2Jan1970 <=0) {
729 /*
730 * DST was not in effect back in 2 Jan. 1970.
731 * Use the offset back then as the GMT offset,
732 * assuming the time zone has not changed since then.
733 */
734 retVal.tp_gmt_offset = offset2Jan1970;
735 retVal.tp_dst_offset = offsetNew - offset2Jan1970;
736 } else {
737 /*
738 * DST was also in effect back in 2 Jan. 1970.
739 * Then our clever trick (or rather, ugly hack) fails.
740 * We will just assume DST offset is an hour.
741 */
742 retVal.tp_gmt_offset = offsetNew - 3600;
743 retVal.tp_dst_offset = 3600;
744 }
745 }
746
747 return retVal;
748}
749
750#endif /* defined(XP_UNIX) || defined(XP_PC) */
751
752/*
753 *------------------------------------------------------------------------
754 *
755 * PR_USPacificTimeParameters --
756 *
757 * The time parameters function for the US Pacific Time Zone.
758 *
759 *------------------------------------------------------------------------
760 */
761
762/*
763 * Returns the mday of the first sunday of the month, where
764 * mday and wday are for a given day in the month.
765 * mdays start with 1 (e.g. 1..31).
766 * wdays start with 0 and are in the range 0..6. 0 = Sunday.
767 */
768#define firstSunday(mday, wday)(((mday - wday + 7 - 1) % 7) + 1) (((mday - wday + 7 - 1) % 7) + 1)
769
770/*
771 * Returns the mday for the N'th Sunday of the month, where
772 * mday and wday are for a given day in the month.
773 * mdays start with 1 (e.g. 1..31).
774 * wdays start with 0 and are in the range 0..6. 0 = Sunday.
775 * N has the following values: 0 = first, 1 = second (etc), -1 = last.
776 * ndays is the number of days in that month, the same value as the
777 * mday of the last day of the month.
778 */
779static PRInt32
780NthSunday(PRInt32 mday, PRInt32 wday, PRInt32 N, PRInt32 ndays)
781{
782 PRInt32 firstSun = firstSunday(mday, wday)(((mday - wday + 7 - 1) % 7) + 1);
783
784 if (N < 0) {
785 N = (ndays - firstSun) / 7;
786 }
787 return firstSun + (7 * N);
788}
789
790typedef struct DSTParams {
791 PRInt8 dst_start_month; /* 0 = January */
792 PRInt8 dst_start_Nth_Sunday; /* N as defined above */
793 PRInt8 dst_start_month_ndays; /* ndays as defined above */
794 PRInt8 dst_end_month; /* 0 = January */
795 PRInt8 dst_end_Nth_Sunday; /* N as defined above */
796 PRInt8 dst_end_month_ndays; /* ndays as defined above */
797} DSTParams;
798
799static const DSTParams dstParams[2] = {
800 /* year < 2007: First April Sunday - Last October Sunday */
801 { 3, 0, 30, 9, -1, 31 },
802 /* year >= 2007: Second March Sunday - First November Sunday */
803 { 2, 1, 31, 10, 0, 30 }
804};
805
806PR_IMPLEMENT(PRTimeParameters)__attribute__((visibility("default"))) PRTimeParameters
807PR_USPacificTimeParameters(const PRExplodedTime *gmt)
808{
809 const DSTParams *dst;
810 PRTimeParameters retVal;
811 PRExplodedTime st;
812
813 /*
814 * Based on geographic location and GMT, figure out offset of
815 * standard time from GMT. In this example implementation, we
816 * assume the local time zone is US Pacific Time.
817 */
818
819 retVal.tp_gmt_offset = -8L * 3600L;
820
821 /*
822 * Make a copy of GMT. Note that the tm_params field of this copy
823 * is ignored.
824 */
825
826 st.tm_usec = gmt->tm_usec;
827 st.tm_sec = gmt->tm_sec;
828 st.tm_min = gmt->tm_min;
829 st.tm_hour = gmt->tm_hour;
830 st.tm_mday = gmt->tm_mday;
831 st.tm_month = gmt->tm_month;
832 st.tm_year = gmt->tm_year;
833 st.tm_wday = gmt->tm_wday;
834 st.tm_yday = gmt->tm_yday;
835
836 /* Apply the offset to GMT to obtain the local standard time */
837 ApplySecOffset(&st, retVal.tp_gmt_offset);
838
839 if (st.tm_year < 2007) { /* first April Sunday - Last October Sunday */
840 dst = &dstParams[0];
841 } else { /* Second March Sunday - First November Sunday */
842 dst = &dstParams[1];
843 }
844
845 /*
846 * Apply the rules on standard time or GMT to obtain daylight saving
847 * time offset. In this implementation, we use the US DST rule.
848 */
849 if (st.tm_month < dst->dst_start_month) {
850 retVal.tp_dst_offset = 0L;
851 } else if (st.tm_month == dst->dst_start_month) {
852 int NthSun = NthSunday(st.tm_mday, st.tm_wday,
853 dst->dst_start_Nth_Sunday,
854 dst->dst_start_month_ndays);
855 if (st.tm_mday < NthSun) { /* Before starting Sunday */
856 retVal.tp_dst_offset = 0L;
857 } else if (st.tm_mday == NthSun) { /* Starting Sunday */
858 /* 01:59:59 PST -> 03:00:00 PDT */
859 if (st.tm_hour < 2) {
860 retVal.tp_dst_offset = 0L;
861 } else {
862 retVal.tp_dst_offset = 3600L;
863 }
864 } else { /* After starting Sunday */
865 retVal.tp_dst_offset = 3600L;
866 }
867 } else if (st.tm_month < dst->dst_end_month) {
868 retVal.tp_dst_offset = 3600L;
869 } else if (st.tm_month == dst->dst_end_month) {
870 int NthSun = NthSunday(st.tm_mday, st.tm_wday,
871 dst->dst_end_Nth_Sunday,
872 dst->dst_end_month_ndays);
873 if (st.tm_mday < NthSun) { /* Before ending Sunday */
874 retVal.tp_dst_offset = 3600L;
875 } else if (st.tm_mday == NthSun) { /* Ending Sunday */
876 /* 01:59:59 PDT -> 01:00:00 PST */
877 if (st.tm_hour < 1) {
878 retVal.tp_dst_offset = 3600L;
879 } else {
880 retVal.tp_dst_offset = 0L;
881 }
882 } else { /* After ending Sunday */
883 retVal.tp_dst_offset = 0L;
884 }
885 } else {
886 retVal.tp_dst_offset = 0L;
887 }
888 return retVal;
889}
890
891/*
892 *------------------------------------------------------------------------
893 *
894 * PR_GMTParameters --
895 *
896 * Returns the PRTimeParameters for Greenwich Mean Time.
897 * Trivially, both the tp_gmt_offset and tp_dst_offset fields are 0.
898 *
899 *------------------------------------------------------------------------
900 */
901
902PR_IMPLEMENT(PRTimeParameters)__attribute__((visibility("default"))) PRTimeParameters
903PR_GMTParameters(const PRExplodedTime *gmt)
904{
905 PRTimeParameters retVal = { 0, 0 };
906 return retVal;
907}
908
909/*
910 * The following code implements PR_ParseTimeString(). It is based on
911 * ns/lib/xp/xp_time.c, revision 1.25, by Jamie Zawinski <jwz@netscape.com>.
912 */
913
914/*
915 * We only recognize the abbreviations of a small subset of time zones
916 * in North America, Europe, and Japan.
917 *
918 * PST/PDT: Pacific Standard/Daylight Time
919 * MST/MDT: Mountain Standard/Daylight Time
920 * CST/CDT: Central Standard/Daylight Time
921 * EST/EDT: Eastern Standard/Daylight Time
922 * AST: Atlantic Standard Time
923 * NST: Newfoundland Standard Time
924 * GMT: Greenwich Mean Time
925 * BST: British Summer Time
926 * MET: Middle Europe Time
927 * EET: Eastern Europe Time
928 * JST: Japan Standard Time
929 */
930
931typedef enum
932{
933 TT_UNKNOWN,
934
935 TT_SUN, TT_MON, TT_TUE, TT_WED, TT_THU, TT_FRI, TT_SAT,
936
937 TT_JAN, TT_FEB, TT_MAR, TT_APR, TT_MAY, TT_JUN,
938 TT_JUL, TT_AUG, TT_SEP, TT_OCT, TT_NOV, TT_DEC,
939
940 TT_PST, TT_PDT, TT_MST, TT_MDT, TT_CST, TT_CDT, TT_EST, TT_EDT,
941 TT_AST, TT_NST, TT_GMT, TT_BST, TT_MET, TT_EET, TT_JST
942} TIME_TOKEN;
943
944/*
945 * This parses a time/date string into a PRTime
946 * (microseconds after "1-Jan-1970 00:00:00 GMT").
947 * It returns PR_SUCCESS on success, and PR_FAILURE
948 * if the time/date string can't be parsed.
949 *
950 * Many formats are handled, including:
951 *
952 * 14 Apr 89 03:20:12
953 * 14 Apr 89 03:20 GMT
954 * Fri, 17 Mar 89 4:01:33
955 * Fri, 17 Mar 89 4:01 GMT
956 * Mon Jan 16 16:12 PDT 1989
957 * Mon Jan 16 16:12 +0130 1989
958 * 6 May 1992 16:41-JST (Wednesday)
959 * 22-AUG-1993 10:59:12.82
960 * 22-AUG-1993 10:59pm
961 * 22-AUG-1993 12:59am
962 * 22-AUG-1993 12:59 PM
963 * Friday, August 04, 1995 3:54 PM
964 * 06/21/95 04:24:34 PM
965 * 20/06/95 21:07
966 * 95-06-08 19:32:48 EDT
967 *
968 * If the input string doesn't contain a description of the timezone,
969 * we consult the `default_to_gmt' to decide whether the string should
970 * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE).
971 * The correct value for this argument depends on what standard specified
972 * the time string which you are parsing.
973 */
974
975PR_IMPLEMENT(PRStatus)__attribute__((visibility("default"))) PRStatus
976PR_ParseTimeStringToExplodedTime(
977 const char *string,
978 PRBool default_to_gmt,
979 PRExplodedTime *result)
980{
981 TIME_TOKEN dotw = TT_UNKNOWN;
982 TIME_TOKEN month = TT_UNKNOWN;
983 TIME_TOKEN zone = TT_UNKNOWN;
984 int zone_offset = -1;
985 int dst_offset = 0;
986 int date = -1;
987 PRInt32 year = -1;
988 int hour = -1;
989 int min = -1;
990 int sec = -1;
991 struct tm *localTimeResult;
992
993 const char *rest = string;
994
995 int iterations = 0;
996
997 PR_ASSERT(string && result)((string && result)?((void)0):PR_Assert("string && result"
,"../../../../pr/src/misc/prtime.c",997))
;
998 if (!string || !result) {
999 return PR_FAILURE;
1000 }
1001
1002 while (*rest)
1003 {
1004
1005 if (iterations++ > 1000)
1006 {
1007 return PR_FAILURE;
1008 }
1009
1010 switch (*rest)
1011 {
1012 case 'a': case 'A':
1013 if (month == TT_UNKNOWN &&
1014 (rest[1] == 'p' || rest[1] == 'P') &&
1015 (rest[2] == 'r' || rest[2] == 'R')) {
1016 month = TT_APR;
1017 }
1018 else if (zone == TT_UNKNOWN &&
1019 (rest[1] == 's' || rest[1] == 'S') &&
1020 (rest[2] == 't' || rest[2] == 'T')) {
1021 zone = TT_AST;
1022 }
1023 else if (month == TT_UNKNOWN &&
1024 (rest[1] == 'u' || rest[1] == 'U') &&
1025 (rest[2] == 'g' || rest[2] == 'G')) {
1026 month = TT_AUG;
1027 }
1028 break;
1029 case 'b': case 'B':
1030 if (zone == TT_UNKNOWN &&
1031 (rest[1] == 's' || rest[1] == 'S') &&
1032 (rest[2] == 't' || rest[2] == 'T')) {
1033 zone = TT_BST;
1034 }
1035 break;
1036 case 'c': case 'C':
1037 if (zone == TT_UNKNOWN &&
1038 (rest[1] == 'd' || rest[1] == 'D') &&
1039 (rest[2] == 't' || rest[2] == 'T')) {
1040 zone = TT_CDT;
1041 }
1042 else if (zone == TT_UNKNOWN &&
1043 (rest[1] == 's' || rest[1] == 'S') &&
1044 (rest[2] == 't' || rest[2] == 'T')) {
1045 zone = TT_CST;
1046 }
1047 break;
1048 case 'd': case 'D':
1049 if (month == TT_UNKNOWN &&
1050 (rest[1] == 'e' || rest[1] == 'E') &&
1051 (rest[2] == 'c' || rest[2] == 'C')) {
1052 month = TT_DEC;
1053 }
1054 break;
1055 case 'e': case 'E':
1056 if (zone == TT_UNKNOWN &&
1057 (rest[1] == 'd' || rest[1] == 'D') &&
1058 (rest[2] == 't' || rest[2] == 'T')) {
1059 zone = TT_EDT;
1060 }
1061 else if (zone == TT_UNKNOWN &&
1062 (rest[1] == 'e' || rest[1] == 'E') &&
1063 (rest[2] == 't' || rest[2] == 'T')) {
1064 zone = TT_EET;
1065 }
1066 else if (zone == TT_UNKNOWN &&
1067 (rest[1] == 's' || rest[1] == 'S') &&
1068 (rest[2] == 't' || rest[2] == 'T')) {
1069 zone = TT_EST;
1070 }
1071 break;
1072 case 'f': case 'F':
1073 if (month == TT_UNKNOWN &&
1074 (rest[1] == 'e' || rest[1] == 'E') &&
1075 (rest[2] == 'b' || rest[2] == 'B')) {
1076 month = TT_FEB;
1077 }
1078 else if (dotw == TT_UNKNOWN &&
1079 (rest[1] == 'r' || rest[1] == 'R') &&
1080 (rest[2] == 'i' || rest[2] == 'I')) {
1081 dotw = TT_FRI;
1082 }
1083 break;
1084 case 'g': case 'G':
1085 if (zone == TT_UNKNOWN &&
1086 (rest[1] == 'm' || rest[1] == 'M') &&
1087 (rest[2] == 't' || rest[2] == 'T')) {
1088 zone = TT_GMT;
1089 }
1090 break;
1091 case 'j': case 'J':
1092 if (month == TT_UNKNOWN &&
1093 (rest[1] == 'a' || rest[1] == 'A') &&
1094 (rest[2] == 'n' || rest[2] == 'N')) {
1095 month = TT_JAN;
1096 }
1097 else if (zone == TT_UNKNOWN &&
1098 (rest[1] == 's' || rest[1] == 'S') &&
1099 (rest[2] == 't' || rest[2] == 'T')) {
1100 zone = TT_JST;
1101 }
1102 else if (month == TT_UNKNOWN &&
1103 (rest[1] == 'u' || rest[1] == 'U') &&
1104 (rest[2] == 'l' || rest[2] == 'L')) {
1105 month = TT_JUL;
1106 }
1107 else if (month == TT_UNKNOWN &&
1108 (rest[1] == 'u' || rest[1] == 'U') &&
1109 (rest[2] == 'n' || rest[2] == 'N')) {
1110 month = TT_JUN;
1111 }
1112 break;
1113 case 'm': case 'M':
1114 if (month == TT_UNKNOWN &&
1115 (rest[1] == 'a' || rest[1] == 'A') &&
1116 (rest[2] == 'r' || rest[2] == 'R')) {
1117 month = TT_MAR;
1118 }
1119 else if (month == TT_UNKNOWN &&
1120 (rest[1] == 'a' || rest[1] == 'A') &&
1121 (rest[2] == 'y' || rest[2] == 'Y')) {
1122 month = TT_MAY;
1123 }
1124 else if (zone == TT_UNKNOWN &&
1125 (rest[1] == 'd' || rest[1] == 'D') &&
1126 (rest[2] == 't' || rest[2] == 'T')) {
1127 zone = TT_MDT;
1128 }
1129 else if (zone == TT_UNKNOWN &&
1130 (rest[1] == 'e' || rest[1] == 'E') &&
1131 (rest[2] == 't' || rest[2] == 'T')) {
1132 zone = TT_MET;
1133 }
1134 else if (dotw == TT_UNKNOWN &&
1135 (rest[1] == 'o' || rest[1] == 'O') &&
1136 (rest[2] == 'n' || rest[2] == 'N')) {
1137 dotw = TT_MON;
1138 }
1139 else if (zone == TT_UNKNOWN &&
1140 (rest[1] == 's' || rest[1] == 'S') &&
1141 (rest[2] == 't' || rest[2] == 'T')) {
1142 zone = TT_MST;
1143 }
1144 break;
1145 case 'n': case 'N':
1146 if (month == TT_UNKNOWN &&
1147 (rest[1] == 'o' || rest[1] == 'O') &&
1148 (rest[2] == 'v' || rest[2] == 'V')) {
1149 month = TT_NOV;
1150 }
1151 else if (zone == TT_UNKNOWN &&
1152 (rest[1] == 's' || rest[1] == 'S') &&
1153 (rest[2] == 't' || rest[2] == 'T')) {
1154 zone = TT_NST;
1155 }
1156 break;
1157 case 'o': case 'O':
1158 if (month == TT_UNKNOWN &&
1159 (rest[1] == 'c' || rest[1] == 'C') &&
1160 (rest[2] == 't' || rest[2] == 'T')) {
1161 month = TT_OCT;
1162 }
1163 break;
1164 case 'p': case 'P':
1165 if (zone == TT_UNKNOWN &&
1166 (rest[1] == 'd' || rest[1] == 'D') &&
1167 (rest[2] == 't' || rest[2] == 'T')) {
1168 zone = TT_PDT;
1169 }
1170 else if (zone == TT_UNKNOWN &&
1171 (rest[1] == 's' || rest[1] == 'S') &&
1172 (rest[2] == 't' || rest[2] == 'T')) {
1173 zone = TT_PST;
1174 }
1175 break;
1176 case 's': case 'S':
1177 if (dotw == TT_UNKNOWN &&
1178 (rest[1] == 'a' || rest[1] == 'A') &&
1179 (rest[2] == 't' || rest[2] == 'T')) {
1180 dotw = TT_SAT;
1181 }
1182 else if (month == TT_UNKNOWN &&
1183 (rest[1] == 'e' || rest[1] == 'E') &&
1184 (rest[2] == 'p' || rest[2] == 'P')) {
1185 month = TT_SEP;
1186 }
1187 else if (dotw == TT_UNKNOWN &&
1188 (rest[1] == 'u' || rest[1] == 'U') &&
1189 (rest[2] == 'n' || rest[2] == 'N')) {
1190 dotw = TT_SUN;
1191 }
1192 break;
1193 case 't': case 'T':
1194 if (dotw == TT_UNKNOWN &&
1195 (rest[1] == 'h' || rest[1] == 'H') &&
1196 (rest[2] == 'u' || rest[2] == 'U')) {
1197 dotw = TT_THU;
1198 }
1199 else if (dotw == TT_UNKNOWN &&
1200 (rest[1] == 'u' || rest[1] == 'U') &&
1201 (rest[2] == 'e' || rest[2] == 'E')) {
1202 dotw = TT_TUE;
1203 }
1204 break;
1205 case 'u': case 'U':
1206 if (zone == TT_UNKNOWN &&
1207 (rest[1] == 't' || rest[1] == 'T') &&
1208 !(rest[2] >= 'A' && rest[2] <= 'Z') &&
1209 !(rest[2] >= 'a' && rest[2] <= 'z'))
1210 /* UT is the same as GMT but UTx is not. */
1211 {
1212 zone = TT_GMT;
1213 }
1214 break;
1215 case 'w': case 'W':
1216 if (dotw == TT_UNKNOWN &&
1217 (rest[1] == 'e' || rest[1] == 'E') &&
1218 (rest[2] == 'd' || rest[2] == 'D')) {
1219 dotw = TT_WED;
1220 }
1221 break;
1222
1223 case '+': case '-':
1224 {
1225 const char *end;
1226 int sign;
1227 if (zone_offset != -1)
1228 {
1229 /* already got one... */
1230 rest++;
1231 break;
1232 }
1233 if (zone != TT_UNKNOWN && zone != TT_GMT)
1234 {
1235 /* GMT+0300 is legal, but PST+0300 is not. */
1236 rest++;
1237 break;
1238 }
1239
1240 sign = ((*rest == '+') ? 1 : -1);
1241 rest++; /* move over sign */
1242 end = rest;
1243 while (*end >= '0' && *end <= '9') {
1244 end++;
1245 }
1246 if (rest == end) { /* no digits here */
1247 break;
1248 }
1249
1250 if ((end - rest) == 4)
1251 /* offset in HHMM */
1252 zone_offset = (((((rest[0]-'0')*10) + (rest[1]-'0')) * 60) +
1253 (((rest[2]-'0')*10) + (rest[3]-'0')));
1254 else if ((end - rest) == 2)
1255 /* offset in hours */
1256 {
1257 zone_offset = (((rest[0]-'0')*10) + (rest[1]-'0')) * 60;
1258 }
1259 else if ((end - rest) == 1)
1260 /* offset in hours */
1261 {
1262 zone_offset = (rest[0]-'0') * 60;
1263 }
1264 else
1265 /* 3 or >4 */
1266 {
1267 break;
1268 }
1269
1270 zone_offset *= sign;
1271 zone = TT_GMT;
1272 break;
1273 }
1274
1275 case '0': case '1': case '2': case '3': case '4':
1276 case '5': case '6': case '7': case '8': case '9':
1277 {
1278 int tmp_hour = -1;
1279 int tmp_min = -1;
1280 int tmp_sec = -1;
1281 const char *end = rest + 1;
1282 while (*end >= '0' && *end <= '9') {
1283 end++;
1284 }
1285
1286 /* end is now the first character after a range of digits. */
1287
1288 if (*end == ':')
1289 {
1290 if (hour >= 0 && min >= 0) { /* already got it */
1291 break;
1292 }
1293
1294 /* We have seen "[0-9]+:", so this is probably HH:MM[:SS] */
1295 if ((end - rest) > 2)
1296 /* it is [0-9][0-9][0-9]+: */
1297 {
1298 break;
1299 }
1300 if ((end - rest) == 2)
1301 tmp_hour = ((rest[0]-'0')*10 +
1302 (rest[1]-'0'));
1303 else {
1304 tmp_hour = (rest[0]-'0');
1305 }
1306
1307 /* move over the colon, and parse minutes */
1308
1309 rest = ++end;
1310 while (*end >= '0' && *end <= '9') {
1311 end++;
1312 }
1313
1314 if (end == rest)
1315 /* no digits after first colon? */
1316 {
1317 break;
1318 }
1319 if ((end - rest) > 2)
1320 /* it is [0-9][0-9][0-9]+: */
1321 {
1322 break;
1323 }
1324 if ((end - rest) == 2)
1325 tmp_min = ((rest[0]-'0')*10 +
1326 (rest[1]-'0'));
1327 else {
1328 tmp_min = (rest[0]-'0');
1329 }
1330
1331 /* now go for seconds */
1332 rest = end;
1333 if (*rest == ':') {
1334 rest++;
1335 }
1336 end = rest;
1337 while (*end >= '0' && *end <= '9') {
1338 end++;
1339 }
1340
1341 if (end == rest)
1342 /* no digits after second colon - that's ok. */
1343 ;
1344 else if ((end - rest) > 2)
1345 /* it is [0-9][0-9][0-9]+: */
1346 {
1347 break;
1348 }
1349 if ((end - rest) == 2)
1350 tmp_sec = ((rest[0]-'0')*10 +
1351 (rest[1]-'0'));
1352 else {
1353 tmp_sec = (rest[0]-'0');
1354 }
1355
1356 /* If we made it here, we've parsed hour and min,
1357 and possibly sec, so it worked as a unit. */
1358
1359 /* skip over whitespace and see if there's an AM or PM
1360 directly following the time.
1361 */
1362 if (tmp_hour <= 12)
1363 {
1364 const char *s = end;
1365 while (*s && (*s == ' ' || *s == '\t')) {
1366 s++;
1367 }
1368 if ((s[0] == 'p' || s[0] == 'P') &&
1369 (s[1] == 'm' || s[1] == 'M'))
1370 /* 10:05pm == 22:05, and 12:05pm == 12:05 */
1371 {
1372 tmp_hour = (tmp_hour == 12 ? 12 : tmp_hour + 12);
1373 }
1374 else if (tmp_hour == 12 &&
1375 (s[0] == 'a' || s[0] == 'A') &&
1376 (s[1] == 'm' || s[1] == 'M'))
1377 /* 12:05am == 00:05 */
1378 {
1379 tmp_hour = 0;
1380 }
1381 }
1382
1383 hour = tmp_hour;
1384 min = tmp_min;
1385 sec = tmp_sec;
1386 rest = end;
1387 break;
1388 }
1389 if ((*end == '/' || *end == '-') &&
1390 end[1] >= '0' && end[1] <= '9')
1391 {
1392 /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95
1393 or even 95-06-05...
1394 #### But it doesn't handle 1995-06-22.
1395 */
1396 int n1, n2, n3;
1397 const char *s;
1398
1399 if (month != TT_UNKNOWN)
1400 /* if we saw a month name, this can't be. */
1401 {
1402 break;
1403 }
1404
1405 s = rest;
1406
1407 n1 = (*s++ - '0'); /* first 1 or 2 digits */
1408 if (*s >= '0' && *s <= '9') {
1409 n1 = n1*10 + (*s++ - '0');
1410 }
1411
1412 if (*s != '/' && *s != '-') { /* slash */
1413 break;
1414 }
1415 s++;
1416
1417 if (*s < '0' || *s > '9') { /* second 1 or 2 digits */
1418 break;
1419 }
1420 n2 = (*s++ - '0');
1421 if (*s >= '0' && *s <= '9') {
1422 n2 = n2*10 + (*s++ - '0');
1423 }
1424
1425 if (*s != '/' && *s != '-') { /* slash */
1426 break;
1427 }
1428 s++;
1429
1430 if (*s < '0' || *s > '9') { /* third 1, 2, 4, or 5 digits */
1431 break;
1432 }
1433 n3 = (*s++ - '0');
1434 if (*s >= '0' && *s <= '9') {
1435 n3 = n3*10 + (*s++ - '0');
1436 }
1437
1438 if (*s >= '0' && *s <= '9') /* optional digits 3, 4, and 5 */
1439 {
1440 n3 = n3*10 + (*s++ - '0');
1441 if (*s < '0' || *s > '9') {
1442 break;
1443 }
1444 n3 = n3*10 + (*s++ - '0');
1445 if (*s >= '0' && *s <= '9') {
1446 n3 = n3*10 + (*s++ - '0');
1447 }
1448 }
1449
1450 if ((*s >= '0' && *s <= '9') || /* followed by non-alphanum */
1451 (*s >= 'A' && *s <= 'Z') ||
1452 (*s >= 'a' && *s <= 'z')) {
1453 break;
1454 }
1455
1456 /* Ok, we parsed three 1-2 digit numbers, with / or -
1457 between them. Now decide what the hell they are
1458 (DD/MM/YY or MM/DD/YY or YY/MM/DD.)
1459 */
1460
1461 if (n1 > 31 || n1 == 0) /* must be YY/MM/DD */
1462 {
1463 if (n2 > 12) {
1464 break;
1465 }
1466 if (n3 > 31) {
1467 break;
1468 }
1469 year = n1;
1470 if (year < 70) {
1471 year += 2000;
1472 }
1473 else if (year < 100) {
1474 year += 1900;
1475 }
1476 month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
1477 date = n3;
1478 rest = s;
1479 break;
1480 }
1481
1482 if (n1 > 12 && n2 > 12) /* illegal */
1483 {
1484 rest = s;
1485 break;
1486 }
1487
1488 if (n3 < 70) {
1489 n3 += 2000;
1490 }
1491 else if (n3 < 100) {
1492 n3 += 1900;
1493 }
1494
1495 if (n1 > 12) /* must be DD/MM/YY */
1496 {
1497 date = n1;
1498 month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
1499 year = n3;
1500 }
1501 else /* assume MM/DD/YY */
1502 {
1503 /* #### In the ambiguous case, should we consult the
1504 locale to find out the local default? */
1505 month = (TIME_TOKEN)(n1 + ((int)TT_JAN) - 1);
1506 date = n2;
1507 year = n3;
1508 }
1509 rest = s;
1510 }
1511 else if ((*end >= 'A' && *end <= 'Z') ||
1512 (*end >= 'a' && *end <= 'z'))
1513 /* Digits followed by non-punctuation - what's that? */
1514 ;
1515 else if ((end - rest) == 5) /* five digits is a year */
1516 year = (year < 0
1517 ? ((rest[0]-'0')*10000L +
1518 (rest[1]-'0')*1000L +
1519 (rest[2]-'0')*100L +
1520 (rest[3]-'0')*10L +
1521 (rest[4]-'0'))
1522 : year);
1523 else if ((end - rest) == 4) /* four digits is a year */
1524 year = (year < 0
1525 ? ((rest[0]-'0')*1000L +
1526 (rest[1]-'0')*100L +
1527 (rest[2]-'0')*10L +
1528 (rest[3]-'0'))
1529 : year);
1530 else if ((end - rest) == 2) /* two digits - date or year */
1531 {
1532 int n = ((rest[0]-'0')*10 +
1533 (rest[1]-'0'));
1534 /* If we don't have a date (day of the month) and we see a number
1535 less than 32, then assume that is the date.
1536
1537 Otherwise, if we have a date and not a year, assume this is the
1538 year. If it is less than 70, then assume it refers to the 21st
1539 century. If it is two digits (>= 70), assume it refers to this
1540 century. Otherwise, assume it refers to an unambiguous year.
1541
1542 The world will surely end soon.
1543 */
1544 if (date < 0 && n < 32) {
1545 date = n;
1546 }
1547 else if (year < 0)
1548 {
1549 if (n < 70) {
1550 year = 2000 + n;
1551 }
1552 else if (n < 100) {
1553 year = 1900 + n;
1554 }
1555 else {
1556 year = n;
1557 }
1558 }
1559 /* else what the hell is this. */
1560 }
1561 else if ((end - rest) == 1) { /* one digit - date */
1562 date = (date < 0 ? (rest[0]-'0') : date);
1563 }
1564 /* else, three or more than five digits - what's that? */
1565
1566 break;
1567 }
1568 }
1569
1570 /* Skip to the end of this token, whether we parsed it or not.
1571 Tokens are delimited by whitespace, or ,;-/
1572 But explicitly not :+-.
1573 */
1574 while (*rest &&
1575 *rest != ' ' && *rest != '\t' &&
1576 *rest != ',' && *rest != ';' &&
1577 *rest != '-' && *rest != '+' &&
1578 *rest != '/' &&
1579 *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']') {
1580 rest++;
1581 }
1582 /* skip over uninteresting chars. */
1583SKIP_MORE:
1584 while (*rest &&
1585 (*rest == ' ' || *rest == '\t' ||
1586 *rest == ',' || *rest == ';' || *rest == '/' ||
1587 *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']')) {
1588 rest++;
1589 }
1590
1591 /* "-" is ignored at the beginning of a token if we have not yet
1592 parsed a year (e.g., the second "-" in "30-AUG-1966"), or if
1593 the character after the dash is not a digit. */
1594 if (*rest == '-' && ((rest > string &&
1595 isalpha((unsigned char)rest[-1])((*__ctype_b_loc ())[(int) (((unsigned char)rest[-1]))] &
(unsigned short int) _ISalpha)
&& year < 0) ||
1596 rest[1] < '0' || rest[1] > '9'))
1597 {
1598 rest++;
1599 goto SKIP_MORE;
1600 }
1601
1602 }
1603
1604 if (zone != TT_UNKNOWN && zone_offset == -1)
1605 {
1606 switch (zone)
1607 {
1608 case TT_PST: zone_offset = -8 * 60; break;
1609 case TT_PDT: zone_offset = -8 * 60; dst_offset = 1 * 60; break;
1610 case TT_MST: zone_offset = -7 * 60; break;
1611 case TT_MDT: zone_offset = -7 * 60; dst_offset = 1 * 60; break;
1612 case TT_CST: zone_offset = -6 * 60; break;
1613 case TT_CDT: zone_offset = -6 * 60; dst_offset = 1 * 60; break;
1614 case TT_EST: zone_offset = -5 * 60; break;
1615 case TT_EDT: zone_offset = -5 * 60; dst_offset = 1 * 60; break;
1616 case TT_AST: zone_offset = -4 * 60; break;
1617 case TT_NST: zone_offset = -3 * 60 - 30; break;
1618 case TT_GMT: zone_offset = 0 * 60; break;
1619 case TT_BST: zone_offset = 0 * 60; dst_offset = 1 * 60; break;
1620 case TT_MET: zone_offset = 1 * 60; break;
1621 case TT_EET: zone_offset = 2 * 60; break;
1622 case TT_JST: zone_offset = 9 * 60; break;
1623 default:
1624 PR_ASSERT (0)((0)?((void)0):PR_Assert("0","../../../../pr/src/misc/prtime.c"
,1624))
;
1625 break;
1626 }
1627 }
1628
1629 /* If we didn't find a year, month, or day-of-the-month, we can't
1630 possibly parse this, and in fact, mktime() will do something random
1631 (I'm seeing it return "Tue Feb 5 06:28:16 2036", which is no doubt
1632 a numerologically significant date... */
1633 if (month == TT_UNKNOWN || date == -1 || year == -1 || year > PR_INT16_MAX32767) {
1634 return PR_FAILURE;
1635 }
1636
1637 memset(result, 0, sizeof(*result));
1638 if (sec != -1) {
1639 result->tm_sec = sec;
1640 }
1641 if (min != -1) {
1642 result->tm_min = min;
1643 }
1644 if (hour != -1) {
1645 result->tm_hour = hour;
1646 }
1647 if (date != -1) {
1648 result->tm_mday = date;
1649 }
1650 if (month != TT_UNKNOWN) {
1651 result->tm_month = (((int)month) - ((int)TT_JAN));
1652 }
1653 if (year != -1) {
1654 result->tm_year = year;
1655 }
1656 if (dotw != TT_UNKNOWN) {
1657 result->tm_wday = (((int)dotw) - ((int)TT_SUN));
1658 }
1659 /*
1660 * Mainly to compute wday and yday, but normalized time is also required
1661 * by the check below that works around a Visual C++ 2005 mktime problem.
1662 */
1663 PR_NormalizeTime(result, PR_GMTParameters);
1664 /* The remaining work is to set the gmt and dst offsets in tm_params. */
1665
1666 if (zone == TT_UNKNOWN && default_to_gmt)
1667 {
1668 /* No zone was specified, so pretend the zone was GMT. */
1669 zone = TT_GMT;
Value stored to 'zone' is never read
1670 zone_offset = 0;
1671 }
1672
1673 if (zone_offset == -1)
1674 {
1675 /* no zone was specified, and we're to assume that everything
1676 is local. */
1677 struct tm localTime;
1678 time_t secs;
1679
1680 PR_ASSERT(result->tm_month > -1 &&((result->tm_month > -1 && result->tm_mday >
0 && result->tm_hour > -1 && result->
tm_min > -1 && result->tm_sec > -1)?((void)0
):PR_Assert("result->tm_month > -1 && result->tm_mday > 0 && result->tm_hour > -1 && result->tm_min > -1 && result->tm_sec > -1"
,"../../../../pr/src/misc/prtime.c",1684))
1681 result->tm_mday > 0 &&((result->tm_month > -1 && result->tm_mday >
0 && result->tm_hour > -1 && result->
tm_min > -1 && result->tm_sec > -1)?((void)0
):PR_Assert("result->tm_month > -1 && result->tm_mday > 0 && result->tm_hour > -1 && result->tm_min > -1 && result->tm_sec > -1"
,"../../../../pr/src/misc/prtime.c",1684))
1682 result->tm_hour > -1 &&((result->tm_month > -1 && result->tm_mday >
0 && result->tm_hour > -1 && result->
tm_min > -1 && result->tm_sec > -1)?((void)0
):PR_Assert("result->tm_month > -1 && result->tm_mday > 0 && result->tm_hour > -1 && result->tm_min > -1 && result->tm_sec > -1"
,"../../../../pr/src/misc/prtime.c",1684))
1683 result->tm_min > -1 &&((result->tm_month > -1 && result->tm_mday >
0 && result->tm_hour > -1 && result->
tm_min > -1 && result->tm_sec > -1)?((void)0
):PR_Assert("result->tm_month > -1 && result->tm_mday > 0 && result->tm_hour > -1 && result->tm_min > -1 && result->tm_sec > -1"
,"../../../../pr/src/misc/prtime.c",1684))
1684 result->tm_sec > -1)((result->tm_month > -1 && result->tm_mday >
0 && result->tm_hour > -1 && result->
tm_min > -1 && result->tm_sec > -1)?((void)0
):PR_Assert("result->tm_month > -1 && result->tm_mday > 0 && result->tm_hour > -1 && result->tm_min > -1 && result->tm_sec > -1"
,"../../../../pr/src/misc/prtime.c",1684))
;
1685
1686 /*
1687 * To obtain time_t from a tm structure representing the local
1688 * time, we call mktime(). However, we need to see if we are
1689 * on 1-Jan-1970 or before. If we are, we can't call mktime()
1690 * because mktime() will crash on win16. In that case, we
1691 * calculate zone_offset based on the zone offset at
1692 * 00:00:00, 2 Jan 1970 GMT, and subtract zone_offset from the
1693 * date we are parsing to transform the date to GMT. We also
1694 * do so if mktime() returns (time_t) -1 (time out of range).
1695 */
1696
1697 /* month, day, hours, mins and secs are always non-negative
1698 so we dont need to worry about them. */
1699 if(result->tm_year >= 1970)
1700 {
1701 PRInt64 usec_per_sec;
1702
1703 localTime.tm_sec = result->tm_sec;
1704 localTime.tm_min = result->tm_min;
1705 localTime.tm_hour = result->tm_hour;
1706 localTime.tm_mday = result->tm_mday;
1707 localTime.tm_mon = result->tm_month;
1708 localTime.tm_year = result->tm_year - 1900;
1709 /* Set this to -1 to tell mktime "I don't care". If you set
1710 it to 0 or 1, you are making assertions about whether the
1711 date you are handing it is in daylight savings mode or not;
1712 and if you're wrong, it will "fix" it for you. */
1713 localTime.tm_isdst = -1;
1714
1715#if _MSC_VER == 1400 /* 1400 = Visual C++ 2005 (8.0) */
1716 /*
1717 * mktime will return (time_t) -1 if the input is a date
1718 * after 23:59:59, December 31, 3000, US Pacific Time (not
1719 * UTC as documented):
1720 * http://msdn.microsoft.com/en-us/library/d1y53h2a(VS.80).aspx
1721 * But if the year is 3001, mktime also invokes the invalid
1722 * parameter handler, causing the application to crash. This
1723 * problem has been reported in
1724 * http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=266036.
1725 * We avoid this crash by not calling mktime if the date is
1726 * out of range. To use a simple test that works in any time
1727 * zone, we consider year 3000 out of range as well. (See
1728 * bug 480740.)
1729 */
1730 if (result->tm_year >= 3000) {
1731 /* Emulate what mktime would have done. */
1732 errno(*__errno_location ()) = EINVAL22;
1733 secs = (time_t) -1;
1734 } else {
1735 secs = mktime(&localTime);
1736 }
1737#else
1738 secs = mktime(&localTime);
1739#endif
1740 if (secs != (time_t) -1)
1741 {
1742 PRTime usecs64;
1743 LL_I2L(usecs64, secs)((usecs64) = (PRInt64)(secs));
1744 LL_I2L(usec_per_sec, PR_USEC_PER_SEC)((usec_per_sec) = (PRInt64)(1000000L));
1745 LL_MUL(usecs64, usecs64, usec_per_sec)((usecs64) = (usecs64) * (usec_per_sec));
1746 PR_ExplodeTime(usecs64, PR_LocalTimeParameters, result);
1747 return PR_SUCCESS;
1748 }
1749 }
1750
1751 /* So mktime() can't handle this case. We assume the
1752 zone_offset for the date we are parsing is the same as
1753 the zone offset on 00:00:00 2 Jan 1970 GMT. */
1754 secs = 86400;
1755 localTimeResult = MT_safe_localtimelocaltime_r(&secs, &localTime);
1756 PR_ASSERT(localTimeResult != NULL)((localTimeResult != ((void*)0))?((void)0):PR_Assert("localTimeResult != NULL"
,"../../../../pr/src/misc/prtime.c",1756))
;
1757 if (localTimeResult == NULL((void*)0)) {
1758 return PR_FAILURE;
1759 }
1760 zone_offset = localTime.tm_min
1761 + 60 * localTime.tm_hour
1762 + 1440 * (localTime.tm_mday - 2);
1763 }
1764
1765 result->tm_params.tp_gmt_offset = zone_offset * 60;
1766 result->tm_params.tp_dst_offset = dst_offset * 60;
1767
1768 return PR_SUCCESS;
1769}
1770
1771PR_IMPLEMENT(PRStatus)__attribute__((visibility("default"))) PRStatus
1772PR_ParseTimeString(
1773 const char *string,
1774 PRBool default_to_gmt,
1775 PRTime *result)
1776{
1777 PRExplodedTime tm;
1778 PRStatus rv;
1779
1780 rv = PR_ParseTimeStringToExplodedTime(string,
1781 default_to_gmt,
1782 &tm);
1783 if (rv != PR_SUCCESS) {
1784 return rv;
1785 }
1786
1787 *result = PR_ImplodeTime(&tm);
1788
1789 return PR_SUCCESS;
1790}
1791
1792/*
1793 *******************************************************************
1794 *******************************************************************
1795 **
1796 ** OLD COMPATIBILITY FUNCTIONS
1797 **
1798 *******************************************************************
1799 *******************************************************************
1800 */
1801
1802
1803/*
1804 *-----------------------------------------------------------------------
1805 *
1806 * PR_FormatTime --
1807 *
1808 * Format a time value into a buffer. Same semantics as strftime().
1809 *
1810 *-----------------------------------------------------------------------
1811 */
1812
1813PR_IMPLEMENT(PRUint32)__attribute__((visibility("default"))) PRUint32
1814PR_FormatTime(char *buf, int buflen, const char *fmt,
1815 const PRExplodedTime *time)
1816{
1817 size_t rv;
1818 struct tm a;
1819 struct tm *ap;
1820
1821 if (time) {
1822 ap = &a;
1823 a.tm_sec = time->tm_sec;
1824 a.tm_min = time->tm_min;
1825 a.tm_hour = time->tm_hour;
1826 a.tm_mday = time->tm_mday;
1827 a.tm_mon = time->tm_month;
1828 a.tm_wday = time->tm_wday;
1829 a.tm_year = time->tm_year - 1900;
1830 a.tm_yday = time->tm_yday;
1831 a.tm_isdst = time->tm_params.tp_dst_offset ? 1 : 0;
1832
1833 /*
1834 * On some platforms, for example SunOS 4, struct tm has two
1835 * additional fields: tm_zone and tm_gmtoff.
1836 */
1837
1838#if (__GLIBC__2 >= 2) || defined(NETBSD) \
1839 || defined(OPENBSD) || defined(FREEBSD) \
1840 || defined(DARWIN) || defined(ANDROID)
1841 a.tm_zone = NULL((void*)0);
1842 a.tm_gmtoff = time->tm_params.tp_gmt_offset +
1843 time->tm_params.tp_dst_offset;
1844#endif
1845 } else {
1846 ap = NULL((void*)0);
1847 }
1848
1849 rv = strftime(buf, buflen, fmt, ap);
1850 if (!rv && buf && buflen > 0) {
1851 /*
1852 * When strftime fails, the contents of buf are indeterminate.
1853 * Some callers don't check the return value from this function,
1854 * so store an empty string in buf in case they try to print it.
1855 */
1856 buf[0] = '\0';
1857 }
1858 return rv;
1859}
1860
1861
1862/*
1863 * The following string arrays and macros are used by PR_FormatTimeUSEnglish().
1864 */
1865
1866static const char* abbrevDays[] =
1867{
1868 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
1869};
1870
1871static const char* days[] =
1872{
1873 "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"
1874};
1875
1876static const char* abbrevMonths[] =
1877{
1878 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1879 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1880};
1881
1882static const char* months[] =
1883{
1884 "January", "February", "March", "April", "May", "June",
1885 "July", "August", "September", "October", "November", "December"
1886};
1887
1888
1889/*
1890 * Add a single character to the given buffer, incrementing the buffer pointer
1891 * and decrementing the buffer size. Return 0 on error.
1892 */
1893#define ADDCHAR( buf, bufSize, ch )do { if( bufSize < 1 ) { *(--buf) = '\0'; return 0; } *buf
++ = ch; bufSize--; } while(0)
\
1894do \
1895{ \
1896 if( bufSize < 1 ) \
1897 { \
1898 *(--buf) = '\0'; \
1899 return 0; \
1900 } \
1901 *buf++ = ch; \
1902 bufSize--; \
1903} \
1904while(0)
1905
1906
1907/*
1908 * Add a string to the given buffer, incrementing the buffer pointer
1909 * and decrementing the buffer size appropriately. Return 0 on error.
1910 */
1911#define ADDSTR( buf, bufSize, str )do { PRUint32 strSize = strlen( str ); if( strSize > bufSize
) { if( bufSize==0 ) *(--buf) = '\0'; else *buf = '\0'; return
0; } memcpy(buf, str, strSize); buf += strSize; bufSize -= strSize
; } while(0)
\
1912do \
1913{ \
1914 PRUint32 strSize = strlen( str ); \
1915 if( strSize > bufSize ) \
1916 { \
1917 if( bufSize==0 ) \
1918 *(--buf) = '\0'; \
1919 else \
1920 *buf = '\0'; \
1921 return 0; \
1922 } \
1923 memcpy(buf, str, strSize); \
1924 buf += strSize; \
1925 bufSize -= strSize; \
1926} \
1927while(0)
1928
1929/* Needed by PR_FormatTimeUSEnglish() */
1930static unsigned int pr_WeekOfYear(const PRExplodedTime* time,
1931 unsigned int firstDayOfWeek);
1932
1933
1934/***********************************************************************************
1935 *
1936 * Description:
1937 * This is a dumbed down version of strftime that will format the date in US
1938 * English regardless of the setting of the global locale. This functionality is
1939 * needed to write things like MIME headers which must always be in US English.
1940 *
1941 **********************************************************************************/
1942
1943PR_IMPLEMENT(PRUint32)__attribute__((visibility("default"))) PRUint32
1944PR_FormatTimeUSEnglish( char* buf, PRUint32 bufSize,
1945 const char* format, const PRExplodedTime* time )
1946{
1947 char* bufPtr = buf;
1948 const char* fmtPtr;
1949 char tmpBuf[ 40 ];
1950 const int tmpBufSize = sizeof( tmpBuf );
1951
1952
1953 for( fmtPtr=format; *fmtPtr != '\0'; fmtPtr++ )
1954 {
1955 if( *fmtPtr != '%' )
1956 {
1957 ADDCHAR( bufPtr, bufSize, *fmtPtr )do { if( bufSize < 1 ) { *(--bufPtr) = '\0'; return 0; } *
bufPtr++ = *fmtPtr; bufSize--; } while(0)
;
1958 }
1959 else
1960 {
1961 switch( *(++fmtPtr) )
1962 {
1963 case '%':
1964 /* escaped '%' character */
1965 ADDCHAR( bufPtr, bufSize, '%' )do { if( bufSize < 1 ) { *(--bufPtr) = '\0'; return 0; } *
bufPtr++ = '%'; bufSize--; } while(0)
;
1966 break;
1967
1968 case 'a':
1969 /* abbreviated weekday name */
1970 ADDSTR( bufPtr, bufSize, abbrevDays[ time->tm_wday ] )do { PRUint32 strSize = strlen( abbrevDays[ time->tm_wday ]
); if( strSize > bufSize ) { if( bufSize==0 ) *(--bufPtr)
= '\0'; else *bufPtr = '\0'; return 0; } memcpy(bufPtr, abbrevDays
[ time->tm_wday ], strSize); bufPtr += strSize; bufSize -=
strSize; } while(0)
;
1971 break;
1972
1973 case 'A':
1974 /* full weekday name */
1975 ADDSTR( bufPtr, bufSize, days[ time->tm_wday ] )do { PRUint32 strSize = strlen( days[ time->tm_wday ] ); if
( strSize > bufSize ) { if( bufSize==0 ) *(--bufPtr) = '\0'
; else *bufPtr = '\0'; return 0; } memcpy(bufPtr, days[ time->
tm_wday ], strSize); bufPtr += strSize; bufSize -= strSize; }
while(0)
;
1976 break;
1977
1978 case 'b':
1979 /* abbreviated month name */
1980 ADDSTR( bufPtr, bufSize, abbrevMonths[ time->tm_month ] )do { PRUint32 strSize = strlen( abbrevMonths[ time->tm_month
] ); if( strSize > bufSize ) { if( bufSize==0 ) *(--bufPtr
) = '\0'; else *bufPtr = '\0'; return 0; } memcpy(bufPtr, abbrevMonths
[ time->tm_month ], strSize); bufPtr += strSize; bufSize -=
strSize; } while(0)
;
1981 break;
1982
1983 case 'B':
1984 /* full month name */
1985 ADDSTR(bufPtr, bufSize, months[ time->tm_month ] )do { PRUint32 strSize = strlen( months[ time->tm_month ] )
; if( strSize > bufSize ) { if( bufSize==0 ) *(--bufPtr) =
'\0'; else *bufPtr = '\0'; return 0; } memcpy(bufPtr, months
[ time->tm_month ], strSize); bufPtr += strSize; bufSize -=
strSize; } while(0)
;
1986 break;
1987
1988 case 'c':
1989 /* Date and time. */
1990 PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%a %b %d %H:%M:%S %Y", time );
1991 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
1992 break;
1993
1994 case 'd':
1995 /* day of month ( 01 - 31 ) */
1996 PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_mday );
1997 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
1998 break;
1999
2000 case 'H':
2001 /* hour ( 00 - 23 ) */
2002 PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_hour );
2003 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
2004 break;
2005
2006 case 'I':
2007 /* hour ( 01 - 12 ) */
2008 PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",
2009 (time->tm_hour%12) ? time->tm_hour%12 : (PRInt32) 12 );
2010 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
2011 break;
2012
2013 case 'j':
2014 /* day number of year ( 001 - 366 ) */
2015 PR_snprintf(tmpBuf,tmpBufSize,"%.3d",time->tm_yday + 1);
2016 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
2017 break;
2018
2019 case 'm':
2020 /* month number ( 01 - 12 ) */
2021 PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_month+1);
2022 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
2023 break;
2024
2025 case 'M':
2026 /* minute ( 00 - 59 ) */
2027 PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_min );
2028 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
2029 break;
2030
2031 case 'p':
2032 /* locale's equivalent of either AM or PM */
2033 ADDSTR( bufPtr, bufSize, (time->tm_hour<12)?"AM":"PM" )do { PRUint32 strSize = strlen( (time->tm_hour<12)?"AM"
:"PM" ); if( strSize > bufSize ) { if( bufSize==0 ) *(--bufPtr
) = '\0'; else *bufPtr = '\0'; return 0; } memcpy(bufPtr, (time
->tm_hour<12)?"AM":"PM", strSize); bufPtr += strSize; bufSize
-= strSize; } while(0)
;
2034 break;
2035
2036 case 'S':
2037 /* seconds ( 00 - 61 ), allows for leap seconds */
2038 PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_sec );
2039 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
2040 break;
2041
2042 case 'U':
2043 /* week number of year ( 00 - 53 ), Sunday is the first day of week 1 */
2044 PR_snprintf(tmpBuf,tmpBufSize,"%.2d", pr_WeekOfYear( time, 0 ) );
2045 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
2046 break;
2047
2048 case 'w':
2049 /* weekday number ( 0 - 6 ), Sunday = 0 */
2050 PR_snprintf(tmpBuf,tmpBufSize,"%d",time->tm_wday );
2051 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
2052 break;
2053
2054 case 'W':
2055 /* Week number of year ( 00 - 53 ), Monday is the first day of week 1 */
2056 PR_snprintf(tmpBuf,tmpBufSize,"%.2d", pr_WeekOfYear( time, 1 ) );
2057 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
2058 break;
2059
2060 case 'x':
2061 /* Date representation */
2062 PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%m/%d/%y", time );
2063 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
2064 break;
2065
2066 case 'X':
2067 /* Time representation. */
2068 PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%H:%M:%S", time );
2069 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
2070 break;
2071
2072 case 'y':
2073 /* year within century ( 00 - 99 ) */
2074 PR_snprintf(tmpBuf,tmpBufSize,"%.2d",time->tm_year % 100 );
2075 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
2076 break;
2077
2078 case 'Y':
2079 /* year as ccyy ( for example 1986 ) */
2080 PR_snprintf(tmpBuf,tmpBufSize,"%.4d",time->tm_year );
2081 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
2082 break;
2083
2084 case 'Z':
2085 /* Time zone name or no characters if no time zone exists.
2086 * Since time zone name is supposed to be independant of locale, we
2087 * defer to PR_FormatTime() for this option.
2088 */
2089 PR_FormatTime( tmpBuf, tmpBufSize, "%Z", time );
2090 ADDSTR( bufPtr, bufSize, tmpBuf )do { PRUint32 strSize = strlen( tmpBuf ); if( strSize > bufSize
) { if( bufSize==0 ) *(--bufPtr) = '\0'; else *bufPtr = '\0'
; return 0; } memcpy(bufPtr, tmpBuf, strSize); bufPtr += strSize
; bufSize -= strSize; } while(0)
;
2091 break;
2092
2093 default:
2094 /* Unknown format. Simply copy format into output buffer. */
2095 ADDCHAR( bufPtr, bufSize, '%' )do { if( bufSize < 1 ) { *(--bufPtr) = '\0'; return 0; } *
bufPtr++ = '%'; bufSize--; } while(0)
;
2096 ADDCHAR( bufPtr, bufSize, *fmtPtr )do { if( bufSize < 1 ) { *(--bufPtr) = '\0'; return 0; } *
bufPtr++ = *fmtPtr; bufSize--; } while(0)
;
2097 break;
2098
2099 }
2100 }
2101 }
2102
2103 ADDCHAR( bufPtr, bufSize, '\0' )do { if( bufSize < 1 ) { *(--bufPtr) = '\0'; return 0; } *
bufPtr++ = '\0'; bufSize--; } while(0)
;
2104 return (PRUint32)(bufPtr - buf - 1);
2105}
2106
2107
2108
2109/***********************************************************************************
2110 *
2111 * Description:
2112 * Returns the week number of the year (0-53) for the given time. firstDayOfWeek
2113 * is the day on which the week is considered to start (0=Sun, 1=Mon, ...).
2114 * Week 1 starts the first time firstDayOfWeek occurs in the year. In other words,
2115 * a partial week at the start of the year is considered week 0.
2116 *
2117 **********************************************************************************/
2118
2119static unsigned int
2120pr_WeekOfYear(const PRExplodedTime* time, unsigned int firstDayOfWeek)
2121{
2122 int dayOfWeek;
2123 int dayOfYear;
2124
2125 /* Get the day of the year for the given time then adjust it to represent the
2126 * first day of the week containing the given time.
2127 */
2128 dayOfWeek = time->tm_wday - firstDayOfWeek;
2129 if (dayOfWeek < 0) {
2130 dayOfWeek += 7;
2131 }
2132
2133 dayOfYear = time->tm_yday - dayOfWeek;
2134
2135 if( dayOfYear <= 0 )
2136 {
2137 /* If dayOfYear is <= 0, it is in the first partial week of the year. */
2138 return 0;
2139 }
2140
2141 /* Count the number of full weeks ( dayOfYear / 7 ) then add a week if there
2142 * are any days left over ( dayOfYear % 7 ). Because we are only counting to
2143 * the first day of the week containing the given time, rather than to the
2144 * actual day representing the given time, any days in week 0 will be "absorbed"
2145 * as extra days in the given week.
2146 */
2147 return (dayOfYear / 7) + ( (dayOfYear % 7) == 0 ? 0 : 1 );
2148
2149}
2150