Bug Summary

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