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