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