Bug Summary

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