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