1 : /* Portions are Copyright (C) 2007 Google Inc */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Netscape Portable Runtime (NSPR).
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : /*
39 : * prtime.cc --
40 : * NOTE: The original nspr file name is prtime.c
41 : *
42 : * NSPR date and time functions
43 : *
44 : * CVS revision 3.37
45 : */
46 :
47 : /*
48 : * The following functions were copied from the NSPR prtime.c file.
49 : * PR_ParseTimeString
50 : * We inlined the new PR_ParseTimeStringToExplodedTime function to avoid
51 : * copying PR_ExplodeTime and PR_LocalTimeParameters. (The PR_ExplodeTime
52 : * and PR_ImplodeTime calls cancel each other out.)
53 : * PR_NormalizeTime
54 : * PR_GMTParameters
55 : * PR_ImplodeTime
56 : * This was modified to use the Win32 SYSTEMTIME/FILETIME structures
57 : * and the timezone offsets are applied to the FILETIME structure.
58 : * All types and macros are defined in the base/third_party/prtime.h file.
59 : * These have been copied from the following nspr files. We have only copied
60 : * over the types we need.
61 : * 1. prtime.h
62 : * 2. prtypes.h
63 : * 3. prlong.h
64 : */
65 :
66 : #include "base/third_party/nspr/prtime.h"
67 : #include "build/build_config.h"
68 :
69 : #if defined(OS_WIN)
70 : #include <windows.h>
71 : #elif defined(OS_MACOSX)
72 : #include <CoreFoundation/CoreFoundation.h>
73 : #endif
74 : #include <errno.h> /* for EINVAL */
75 : #include <time.h>
76 : #ifdef ANDROID
77 : #include <ctype.h> /* for isalpha() */
78 : #endif
79 :
80 : namespace nspr {
81 :
82 : /* Implements the Unix localtime_r() function for windows */
83 : #if defined(OS_WIN)
84 : static void localtime_r(const time_t* secs, struct tm* time) {
85 : (void) localtime_s(time, secs);
86 : }
87 : #endif
88 :
89 : /*
90 : * The COUNT_LEAPS macro counts the number of leap years passed by
91 : * till the start of the given year Y. At the start of the year 4
92 : * A.D. the number of leap years passed by is 0, while at the start of
93 : * the year 5 A.D. this count is 1. The number of years divisible by
94 : * 100 but not divisible by 400 (the non-leap years) is deducted from
95 : * the count to get the correct number of leap years.
96 : *
97 : * The COUNT_DAYS macro counts the number of days since 01/01/01 till the
98 : * start of the given year Y. The number of days at the start of the year
99 : * 1 is 0 while the number of days at the start of the year 2 is 365
100 : * (which is ((2)-1) * 365) and so on. The reference point is 01/01/01
101 : * midnight 00:00:00.
102 : */
103 :
104 : #define COUNT_LEAPS(Y) ( ((Y)-1)/4 - ((Y)-1)/100 + ((Y)-1)/400 )
105 : #define COUNT_DAYS(Y) ( ((Y)-1)*365 + COUNT_LEAPS(Y) )
106 : #define DAYS_BETWEEN_YEARS(A, B) (COUNT_DAYS(B) - COUNT_DAYS(A))
107 :
108 : /*
109 : *------------------------------------------------------------------------
110 : *
111 : * PR_ImplodeTime --
112 : *
113 : * Cf. time_t mktime(struct tm *tp)
114 : * Note that 1 year has < 2^25 seconds. So an PRInt32 is large enough.
115 : *
116 : *------------------------------------------------------------------------
117 : */
118 : PRTime
119 0 : PR_ImplodeTime(const PRExplodedTime *exploded)
120 : {
121 : // This is important, we want to make sure multiplications are
122 : // done with the correct precision.
123 : static const PRTime kSecondsToMicroseconds = static_cast<PRTime>(1000000);
124 : #if defined(OS_WIN)
125 : // Create the system struct representing our exploded time.
126 : SYSTEMTIME st = {0};
127 : FILETIME ft = {0};
128 : ULARGE_INTEGER uli = {0};
129 :
130 : st.wYear = exploded->tm_year;
131 : st.wMonth = exploded->tm_month + 1;
132 : st.wDayOfWeek = exploded->tm_wday;
133 : st.wDay = exploded->tm_mday;
134 : st.wHour = exploded->tm_hour;
135 : st.wMinute = exploded->tm_min;
136 : st.wSecond = exploded->tm_sec;
137 : st.wMilliseconds = exploded->tm_usec/1000;
138 : // Convert to FILETIME.
139 : if (!SystemTimeToFileTime(&st, &ft)) {
140 : NOTREACHED() << "Unable to convert time";
141 : return 0;
142 : }
143 : // Apply offsets.
144 : uli.LowPart = ft.dwLowDateTime;
145 : uli.HighPart = ft.dwHighDateTime;
146 : // Convert from Windows epoch to NSPR epoch, and 100-nanoseconds units
147 : // to microsecond units.
148 : PRTime result =
149 : static_cast<PRTime>((uli.QuadPart / 10) - GG_LONGLONG(11644473600000000));
150 : // Adjust for time zone and dst. Convert from seconds to microseconds.
151 : result -= (exploded->tm_params.tp_gmt_offset +
152 : exploded->tm_params.tp_dst_offset) * kSecondsToMicroseconds;
153 : return result;
154 : #elif defined(OS_MACOSX)
155 : // Create the system struct representing our exploded time.
156 : CFGregorianDate gregorian_date;
157 : gregorian_date.year = exploded->tm_year;
158 : gregorian_date.month = exploded->tm_month + 1;
159 : gregorian_date.day = exploded->tm_mday;
160 : gregorian_date.hour = exploded->tm_hour;
161 : gregorian_date.minute = exploded->tm_min;
162 : gregorian_date.second = exploded->tm_sec;
163 :
164 : // Compute |absolute_time| in seconds, correct for gmt and dst
165 : // (note the combined offset will be negative when we need to add it), then
166 : // convert to microseconds which is what PRTime expects.
167 : CFAbsoluteTime absolute_time =
168 : CFGregorianDateGetAbsoluteTime(gregorian_date, NULL);
169 : PRTime result = static_cast<PRTime>(absolute_time);
170 : result -= exploded->tm_params.tp_gmt_offset +
171 : exploded->tm_params.tp_dst_offset;
172 : result += kCFAbsoluteTimeIntervalSince1970; // PRTime epoch is 1970
173 : result *= kSecondsToMicroseconds;
174 : result += exploded->tm_usec;
175 : return result;
176 : #elif defined(ANDROID)
177 : #define LL_ADD(r, a, b) ((r) = (a) + (b))
178 : #define LL_SUB(r, a, b) ((r) = (a) - (b))
179 :
180 : PRExplodedTime copy;
181 : PRTime retVal;
182 : PRInt64 secPerDay, usecPerSec;
183 : PRInt64 temp;
184 : PRInt64 numSecs64;
185 : PRInt32 numDays;
186 : PRInt32 numSecs;
187 :
188 : /* Normalize first. Do this on our copy */
189 : copy = *exploded;
190 : PR_NormalizeTime(©, PR_GMTParameters);
191 :
192 : numDays = DAYS_BETWEEN_YEARS(1970, copy.tm_year);
193 :
194 : numSecs = copy.tm_yday * 86400 + copy.tm_hour * 3600
195 : + copy.tm_min * 60 + copy.tm_sec;
196 :
197 : LL_I2L(temp, numDays);
198 : LL_I2L(secPerDay, 86400);
199 : LL_MUL(temp, temp, secPerDay);
200 : LL_I2L(numSecs64, numSecs);
201 : LL_ADD(numSecs64, numSecs64, temp);
202 :
203 : /* apply the GMT and DST offsets */
204 : LL_I2L(temp, copy.tm_params.tp_gmt_offset);
205 : LL_SUB(numSecs64, numSecs64, temp);
206 : LL_I2L(temp, copy.tm_params.tp_dst_offset);
207 : LL_SUB(numSecs64, numSecs64, temp);
208 :
209 : LL_I2L(usecPerSec, 1000000L);
210 : LL_MUL(temp, numSecs64, usecPerSec);
211 : LL_I2L(retVal, copy.tm_usec);
212 : LL_ADD(retVal, retVal, temp);
213 :
214 : return retVal;
215 : #elif defined(OS_LINUX)
216 0 : struct tm exp_tm = {0};
217 0 : exp_tm.tm_sec = exploded->tm_sec;
218 0 : exp_tm.tm_min = exploded->tm_min;
219 0 : exp_tm.tm_hour = exploded->tm_hour;
220 0 : exp_tm.tm_mday = exploded->tm_mday;
221 0 : exp_tm.tm_mon = exploded->tm_month;
222 0 : exp_tm.tm_year = exploded->tm_year - 1900;
223 :
224 : // We assume that time_t is defined as a long.
225 0 : time_t absolute_time = timegm(&exp_tm);
226 :
227 : // If timegm returned -1. Since we don't pass it a time zone, the only
228 : // valid case of returning -1 is 1 second before Epoch (Dec 31, 1969).
229 0 : if (absolute_time == -1 &&
230 : exploded->tm_year != 1969 && exploded->tm_month != 11 &&
231 : exploded->tm_mday != 31 && exploded->tm_hour != 23 &&
232 : exploded->tm_min != 59 && exploded->tm_sec != 59) {
233 : // Date was possibly too far in the future and would overflow. Return
234 : // the most future date possible (year 2038).
235 0 : if (exploded->tm_year >= 1970)
236 0 : return static_cast<PRTime>(LONG_MAX) * kSecondsToMicroseconds;
237 : // Date was possibly too far in the past and would underflow. Return
238 : // the most past date possible (year 1901).
239 0 : return static_cast<PRTime>(LONG_MIN) * kSecondsToMicroseconds;
240 : }
241 :
242 0 : PRTime result = static_cast<PRTime>(absolute_time);
243 : result -= exploded->tm_params.tp_gmt_offset +
244 0 : exploded->tm_params.tp_dst_offset;
245 0 : result *= kSecondsToMicroseconds;
246 0 : result += exploded->tm_usec;
247 0 : return result;
248 : #else
249 : #error No PR_ImplodeTime implemented on your platform.
250 : #endif
251 : }
252 :
253 : /*
254 : * Static variables used by functions in this file
255 : */
256 :
257 : /*
258 : * The following array contains the day of year for the last day of
259 : * each month, where index 1 is January, and day 0 is January 1.
260 : */
261 :
262 : static const int lastDayOfMonth[2][13] = {
263 : {-1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364},
264 : {-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}
265 : };
266 :
267 : /*
268 : * The number of days in a month
269 : */
270 :
271 : static const PRInt8 nDays[2][12] = {
272 : {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
273 : {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
274 : };
275 :
276 : /*
277 : *-------------------------------------------------------------------------
278 : *
279 : * IsLeapYear --
280 : *
281 : * Returns 1 if the year is a leap year, 0 otherwise.
282 : *
283 : *-------------------------------------------------------------------------
284 : */
285 :
286 0 : static int IsLeapYear(PRInt16 year)
287 : {
288 0 : if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
289 0 : return 1;
290 : else
291 0 : return 0;
292 : }
293 :
294 : /*
295 : * 'secOffset' should be less than 86400 (i.e., a day).
296 : * 'time' should point to a normalized PRExplodedTime.
297 : */
298 :
299 : static void
300 0 : ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset)
301 : {
302 0 : time->tm_sec += secOffset;
303 :
304 : /* Note that in this implementation we do not count leap seconds */
305 0 : if (time->tm_sec < 0 || time->tm_sec >= 60) {
306 0 : time->tm_min += time->tm_sec / 60;
307 0 : time->tm_sec %= 60;
308 0 : if (time->tm_sec < 0) {
309 0 : time->tm_sec += 60;
310 0 : time->tm_min--;
311 : }
312 : }
313 :
314 0 : if (time->tm_min < 0 || time->tm_min >= 60) {
315 0 : time->tm_hour += time->tm_min / 60;
316 0 : time->tm_min %= 60;
317 0 : if (time->tm_min < 0) {
318 0 : time->tm_min += 60;
319 0 : time->tm_hour--;
320 : }
321 : }
322 :
323 0 : if (time->tm_hour < 0) {
324 : /* Decrement 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 < 1) {
329 0 : time->tm_month--;
330 0 : if (time->tm_month < 0) {
331 0 : time->tm_month = 11;
332 0 : time->tm_year--;
333 0 : if (IsLeapYear(time->tm_year))
334 0 : time->tm_yday = 365;
335 : else
336 0 : time->tm_yday = 364;
337 : }
338 0 : time->tm_mday = nDays[IsLeapYear(time->tm_year)][time->tm_month];
339 : }
340 0 : time->tm_wday--;
341 0 : if (time->tm_wday < 0)
342 0 : time->tm_wday = 6;
343 0 : } else if (time->tm_hour > 23) {
344 : /* Increment mday, yday, and wday */
345 0 : time->tm_hour -= 24;
346 0 : time->tm_mday++;
347 0 : time->tm_yday++;
348 0 : if (time->tm_mday >
349 0 : nDays[IsLeapYear(time->tm_year)][time->tm_month]) {
350 0 : time->tm_mday = 1;
351 0 : time->tm_month++;
352 0 : if (time->tm_month > 11) {
353 0 : time->tm_month = 0;
354 0 : time->tm_year++;
355 0 : time->tm_yday = 0;
356 : }
357 : }
358 0 : time->tm_wday++;
359 0 : if (time->tm_wday > 6)
360 0 : time->tm_wday = 0;
361 : }
362 0 : }
363 :
364 : void
365 0 : PR_NormalizeTime(PRExplodedTime *time, PRTimeParamFn params)
366 : {
367 : int daysInMonth;
368 : PRInt32 numDays;
369 :
370 : /* Get back to GMT */
371 : time->tm_sec -= time->tm_params.tp_gmt_offset
372 0 : + time->tm_params.tp_dst_offset;
373 0 : time->tm_params.tp_gmt_offset = 0;
374 0 : time->tm_params.tp_dst_offset = 0;
375 :
376 : /* Now normalize GMT */
377 :
378 0 : if (time->tm_usec < 0 || time->tm_usec >= 1000000) {
379 0 : time->tm_sec += time->tm_usec / 1000000;
380 0 : time->tm_usec %= 1000000;
381 0 : if (time->tm_usec < 0) {
382 0 : time->tm_usec += 1000000;
383 0 : time->tm_sec--;
384 : }
385 : }
386 :
387 : /* Note that we do not count leap seconds in this implementation */
388 0 : if (time->tm_sec < 0 || time->tm_sec >= 60) {
389 0 : time->tm_min += time->tm_sec / 60;
390 0 : time->tm_sec %= 60;
391 0 : if (time->tm_sec < 0) {
392 0 : time->tm_sec += 60;
393 0 : time->tm_min--;
394 : }
395 : }
396 :
397 0 : if (time->tm_min < 0 || time->tm_min >= 60) {
398 0 : time->tm_hour += time->tm_min / 60;
399 0 : time->tm_min %= 60;
400 0 : if (time->tm_min < 0) {
401 0 : time->tm_min += 60;
402 0 : time->tm_hour--;
403 : }
404 : }
405 :
406 0 : if (time->tm_hour < 0 || time->tm_hour >= 24) {
407 0 : time->tm_mday += time->tm_hour / 24;
408 0 : time->tm_hour %= 24;
409 0 : if (time->tm_hour < 0) {
410 0 : time->tm_hour += 24;
411 0 : time->tm_mday--;
412 : }
413 : }
414 :
415 : /* Normalize month and year before mday */
416 0 : if (time->tm_month < 0 || time->tm_month >= 12) {
417 0 : time->tm_year += time->tm_month / 12;
418 0 : time->tm_month %= 12;
419 0 : if (time->tm_month < 0) {
420 0 : time->tm_month += 12;
421 0 : time->tm_year--;
422 : }
423 : }
424 :
425 : /* Now that month and year are in proper range, normalize mday */
426 :
427 0 : if (time->tm_mday < 1) {
428 : /* mday too small */
429 0 : do {
430 : /* the previous month */
431 0 : time->tm_month--;
432 0 : if (time->tm_month < 0) {
433 0 : time->tm_month = 11;
434 0 : time->tm_year--;
435 : }
436 0 : time->tm_mday += nDays[IsLeapYear(time->tm_year)][time->tm_month];
437 : } while (time->tm_mday < 1);
438 : } else {
439 0 : daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month];
440 0 : while (time->tm_mday > daysInMonth) {
441 : /* mday too large */
442 0 : time->tm_mday -= daysInMonth;
443 0 : time->tm_month++;
444 0 : if (time->tm_month > 11) {
445 0 : time->tm_month = 0;
446 0 : time->tm_year++;
447 : }
448 0 : daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month];
449 : }
450 : }
451 :
452 : /* Recompute yday and wday */
453 : time->tm_yday = time->tm_mday +
454 0 : lastDayOfMonth[IsLeapYear(time->tm_year)][time->tm_month];
455 :
456 0 : numDays = DAYS_BETWEEN_YEARS(1970, time->tm_year) + time->tm_yday;
457 0 : time->tm_wday = (numDays + 4) % 7;
458 0 : if (time->tm_wday < 0) {
459 0 : time->tm_wday += 7;
460 : }
461 :
462 : /* Recompute time parameters */
463 :
464 0 : time->tm_params = params(time);
465 :
466 : ApplySecOffset(time, time->tm_params.tp_gmt_offset
467 0 : + time->tm_params.tp_dst_offset);
468 0 : }
469 :
470 : /*
471 : *------------------------------------------------------------------------
472 : *
473 : * PR_GMTParameters --
474 : *
475 : * Returns the PRTimeParameters for Greenwich Mean Time.
476 : * Trivially, both the tp_gmt_offset and tp_dst_offset fields are 0.
477 : *
478 : *------------------------------------------------------------------------
479 : */
480 :
481 : PRTimeParameters
482 0 : PR_GMTParameters(const PRExplodedTime *gmt)
483 : {
484 0 : PRTimeParameters retVal = { 0, 0 };
485 : return retVal;
486 : }
487 :
488 : /*
489 : * The following code implements PR_ParseTimeString(). It is based on
490 : * ns/lib/xp/xp_time.c, revision 1.25, by Jamie Zawinski <jwz@netscape.com>.
491 : */
492 :
493 : /*
494 : * We only recognize the abbreviations of a small subset of time zones
495 : * in North America, Europe, and Japan.
496 : *
497 : * PST/PDT: Pacific Standard/Daylight Time
498 : * MST/MDT: Mountain Standard/Daylight Time
499 : * CST/CDT: Central Standard/Daylight Time
500 : * EST/EDT: Eastern Standard/Daylight Time
501 : * AST: Atlantic Standard Time
502 : * NST: Newfoundland Standard Time
503 : * GMT: Greenwich Mean Time
504 : * BST: British Summer Time
505 : * MET: Middle Europe Time
506 : * EET: Eastern Europe Time
507 : * JST: Japan Standard Time
508 : */
509 :
510 : typedef enum
511 : {
512 : TT_UNKNOWN,
513 :
514 : TT_SUN, TT_MON, TT_TUE, TT_WED, TT_THU, TT_FRI, TT_SAT,
515 :
516 : TT_JAN, TT_FEB, TT_MAR, TT_APR, TT_MAY, TT_JUN,
517 : TT_JUL, TT_AUG, TT_SEP, TT_OCT, TT_NOV, TT_DEC,
518 :
519 : TT_PST, TT_PDT, TT_MST, TT_MDT, TT_CST, TT_CDT, TT_EST, TT_EDT,
520 : TT_AST, TT_NST, TT_GMT, TT_BST, TT_MET, TT_EET, TT_JST
521 : } TIME_TOKEN;
522 :
523 : /*
524 : * This parses a time/date string into a PRTime
525 : * (microseconds after "1-Jan-1970 00:00:00 GMT").
526 : * It returns PR_SUCCESS on success, and PR_FAILURE
527 : * if the time/date string can't be parsed.
528 : *
529 : * Many formats are handled, including:
530 : *
531 : * 14 Apr 89 03:20:12
532 : * 14 Apr 89 03:20 GMT
533 : * Fri, 17 Mar 89 4:01:33
534 : * Fri, 17 Mar 89 4:01 GMT
535 : * Mon Jan 16 16:12 PDT 1989
536 : * Mon Jan 16 16:12 +0130 1989
537 : * 6 May 1992 16:41-JST (Wednesday)
538 : * 22-AUG-1993 10:59:12.82
539 : * 22-AUG-1993 10:59pm
540 : * 22-AUG-1993 12:59am
541 : * 22-AUG-1993 12:59 PM
542 : * Friday, August 04, 1995 3:54 PM
543 : * 06/21/95 04:24:34 PM
544 : * 20/06/95 21:07
545 : * 95-06-08 19:32:48 EDT
546 : *
547 : * If the input string doesn't contain a description of the timezone,
548 : * we consult the `default_to_gmt' to decide whether the string should
549 : * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE).
550 : * The correct value for this argument depends on what standard specified
551 : * the time string which you are parsing.
552 : */
553 :
554 : PRStatus
555 0 : PR_ParseTimeString(
556 : const char *string,
557 : bool default_to_gmt,
558 : PRTime *result_imploded)
559 : {
560 : PRExplodedTime tm;
561 0 : PRExplodedTime *result = &tm;
562 0 : TIME_TOKEN dotw = TT_UNKNOWN;
563 0 : TIME_TOKEN month = TT_UNKNOWN;
564 0 : TIME_TOKEN zone = TT_UNKNOWN;
565 0 : int zone_offset = -1;
566 0 : int dst_offset = 0;
567 0 : int date = -1;
568 0 : PRInt32 year = -1;
569 0 : int hour = -1;
570 0 : int min = -1;
571 0 : int sec = -1;
572 :
573 0 : const char *rest = string;
574 :
575 0 : int iterations = 0;
576 :
577 0 : PR_ASSERT(string && result);
578 0 : if (!string || !result) return PR_FAILURE;
579 :
580 0 : while (*rest)
581 : {
582 :
583 0 : if (iterations++ > 1000)
584 : {
585 0 : return PR_FAILURE;
586 : }
587 :
588 0 : switch (*rest)
589 : {
590 : case 'a': case 'A':
591 0 : if (month == TT_UNKNOWN &&
592 0 : (rest[1] == 'p' || rest[1] == 'P') &&
593 0 : (rest[2] == 'r' || rest[2] == 'R'))
594 0 : month = TT_APR;
595 0 : else if (zone == TT_UNKNOWN &&
596 0 : (rest[1] == 's' || rest[1] == 'S') &&
597 0 : (rest[2] == 't' || rest[2] == 'T'))
598 0 : zone = TT_AST;
599 0 : else if (month == TT_UNKNOWN &&
600 0 : (rest[1] == 'u' || rest[1] == 'U') &&
601 0 : (rest[2] == 'g' || rest[2] == 'G'))
602 0 : month = TT_AUG;
603 0 : break;
604 : case 'b': case 'B':
605 0 : if (zone == TT_UNKNOWN &&
606 0 : (rest[1] == 's' || rest[1] == 'S') &&
607 0 : (rest[2] == 't' || rest[2] == 'T'))
608 0 : zone = TT_BST;
609 0 : break;
610 : case 'c': case 'C':
611 0 : if (zone == TT_UNKNOWN &&
612 0 : (rest[1] == 'd' || rest[1] == 'D') &&
613 0 : (rest[2] == 't' || rest[2] == 'T'))
614 0 : zone = TT_CDT;
615 0 : else if (zone == TT_UNKNOWN &&
616 0 : (rest[1] == 's' || rest[1] == 'S') &&
617 0 : (rest[2] == 't' || rest[2] == 'T'))
618 0 : zone = TT_CST;
619 0 : break;
620 : case 'd': case 'D':
621 0 : if (month == TT_UNKNOWN &&
622 0 : (rest[1] == 'e' || rest[1] == 'E') &&
623 0 : (rest[2] == 'c' || rest[2] == 'C'))
624 0 : month = TT_DEC;
625 0 : break;
626 : case 'e': case 'E':
627 0 : if (zone == TT_UNKNOWN &&
628 0 : (rest[1] == 'd' || rest[1] == 'D') &&
629 0 : (rest[2] == 't' || rest[2] == 'T'))
630 0 : zone = TT_EDT;
631 0 : else if (zone == TT_UNKNOWN &&
632 0 : (rest[1] == 'e' || rest[1] == 'E') &&
633 0 : (rest[2] == 't' || rest[2] == 'T'))
634 0 : zone = TT_EET;
635 0 : else if (zone == TT_UNKNOWN &&
636 0 : (rest[1] == 's' || rest[1] == 'S') &&
637 0 : (rest[2] == 't' || rest[2] == 'T'))
638 0 : zone = TT_EST;
639 0 : break;
640 : case 'f': case 'F':
641 0 : if (month == TT_UNKNOWN &&
642 0 : (rest[1] == 'e' || rest[1] == 'E') &&
643 0 : (rest[2] == 'b' || rest[2] == 'B'))
644 0 : month = TT_FEB;
645 0 : else if (dotw == TT_UNKNOWN &&
646 0 : (rest[1] == 'r' || rest[1] == 'R') &&
647 0 : (rest[2] == 'i' || rest[2] == 'I'))
648 0 : dotw = TT_FRI;
649 0 : break;
650 : case 'g': case 'G':
651 0 : if (zone == TT_UNKNOWN &&
652 0 : (rest[1] == 'm' || rest[1] == 'M') &&
653 0 : (rest[2] == 't' || rest[2] == 'T'))
654 0 : zone = TT_GMT;
655 0 : break;
656 : case 'j': case 'J':
657 0 : if (month == TT_UNKNOWN &&
658 0 : (rest[1] == 'a' || rest[1] == 'A') &&
659 0 : (rest[2] == 'n' || rest[2] == 'N'))
660 0 : month = TT_JAN;
661 0 : else if (zone == TT_UNKNOWN &&
662 0 : (rest[1] == 's' || rest[1] == 'S') &&
663 0 : (rest[2] == 't' || rest[2] == 'T'))
664 0 : zone = TT_JST;
665 0 : else if (month == TT_UNKNOWN &&
666 0 : (rest[1] == 'u' || rest[1] == 'U') &&
667 0 : (rest[2] == 'l' || rest[2] == 'L'))
668 0 : month = TT_JUL;
669 0 : else if (month == TT_UNKNOWN &&
670 0 : (rest[1] == 'u' || rest[1] == 'U') &&
671 0 : (rest[2] == 'n' || rest[2] == 'N'))
672 0 : month = TT_JUN;
673 0 : break;
674 : case 'm': case 'M':
675 0 : if (month == TT_UNKNOWN &&
676 0 : (rest[1] == 'a' || rest[1] == 'A') &&
677 0 : (rest[2] == 'r' || rest[2] == 'R'))
678 0 : month = TT_MAR;
679 0 : else if (month == TT_UNKNOWN &&
680 0 : (rest[1] == 'a' || rest[1] == 'A') &&
681 0 : (rest[2] == 'y' || rest[2] == 'Y'))
682 0 : month = TT_MAY;
683 0 : else if (zone == TT_UNKNOWN &&
684 0 : (rest[1] == 'd' || rest[1] == 'D') &&
685 0 : (rest[2] == 't' || rest[2] == 'T'))
686 0 : zone = TT_MDT;
687 0 : else if (zone == TT_UNKNOWN &&
688 0 : (rest[1] == 'e' || rest[1] == 'E') &&
689 0 : (rest[2] == 't' || rest[2] == 'T'))
690 0 : zone = TT_MET;
691 0 : else if (dotw == TT_UNKNOWN &&
692 0 : (rest[1] == 'o' || rest[1] == 'O') &&
693 0 : (rest[2] == 'n' || rest[2] == 'N'))
694 0 : dotw = TT_MON;
695 0 : else if (zone == TT_UNKNOWN &&
696 0 : (rest[1] == 's' || rest[1] == 'S') &&
697 0 : (rest[2] == 't' || rest[2] == 'T'))
698 0 : zone = TT_MST;
699 0 : break;
700 : case 'n': case 'N':
701 0 : if (month == TT_UNKNOWN &&
702 0 : (rest[1] == 'o' || rest[1] == 'O') &&
703 0 : (rest[2] == 'v' || rest[2] == 'V'))
704 0 : month = TT_NOV;
705 0 : else if (zone == TT_UNKNOWN &&
706 0 : (rest[1] == 's' || rest[1] == 'S') &&
707 0 : (rest[2] == 't' || rest[2] == 'T'))
708 0 : zone = TT_NST;
709 0 : break;
710 : case 'o': case 'O':
711 0 : if (month == TT_UNKNOWN &&
712 0 : (rest[1] == 'c' || rest[1] == 'C') &&
713 0 : (rest[2] == 't' || rest[2] == 'T'))
714 0 : month = TT_OCT;
715 0 : break;
716 : case 'p': case 'P':
717 0 : if (zone == TT_UNKNOWN &&
718 0 : (rest[1] == 'd' || rest[1] == 'D') &&
719 0 : (rest[2] == 't' || rest[2] == 'T'))
720 0 : zone = TT_PDT;
721 0 : else if (zone == TT_UNKNOWN &&
722 0 : (rest[1] == 's' || rest[1] == 'S') &&
723 0 : (rest[2] == 't' || rest[2] == 'T'))
724 0 : zone = TT_PST;
725 0 : break;
726 : case 's': case 'S':
727 0 : if (dotw == TT_UNKNOWN &&
728 0 : (rest[1] == 'a' || rest[1] == 'A') &&
729 0 : (rest[2] == 't' || rest[2] == 'T'))
730 0 : dotw = TT_SAT;
731 0 : else if (month == TT_UNKNOWN &&
732 0 : (rest[1] == 'e' || rest[1] == 'E') &&
733 0 : (rest[2] == 'p' || rest[2] == 'P'))
734 0 : month = TT_SEP;
735 0 : else if (dotw == TT_UNKNOWN &&
736 0 : (rest[1] == 'u' || rest[1] == 'U') &&
737 0 : (rest[2] == 'n' || rest[2] == 'N'))
738 0 : dotw = TT_SUN;
739 0 : break;
740 : case 't': case 'T':
741 0 : if (dotw == TT_UNKNOWN &&
742 0 : (rest[1] == 'h' || rest[1] == 'H') &&
743 0 : (rest[2] == 'u' || rest[2] == 'U'))
744 0 : dotw = TT_THU;
745 0 : else if (dotw == TT_UNKNOWN &&
746 0 : (rest[1] == 'u' || rest[1] == 'U') &&
747 0 : (rest[2] == 'e' || rest[2] == 'E'))
748 0 : dotw = TT_TUE;
749 0 : break;
750 : case 'u': case 'U':
751 0 : if (zone == TT_UNKNOWN &&
752 0 : (rest[1] == 't' || rest[1] == 'T') &&
753 0 : !(rest[2] >= 'A' && rest[2] <= 'Z') &&
754 0 : !(rest[2] >= 'a' && rest[2] <= 'z'))
755 : /* UT is the same as GMT but UTx is not. */
756 0 : zone = TT_GMT;
757 0 : break;
758 : case 'w': case 'W':
759 0 : if (dotw == TT_UNKNOWN &&
760 0 : (rest[1] == 'e' || rest[1] == 'E') &&
761 0 : (rest[2] == 'd' || rest[2] == 'D'))
762 0 : dotw = TT_WED;
763 0 : break;
764 :
765 : case '+': case '-':
766 : {
767 : const char *end;
768 : int sign;
769 0 : if (zone_offset != -1)
770 : {
771 : /* already got one... */
772 0 : rest++;
773 0 : break;
774 : }
775 0 : if (zone != TT_UNKNOWN && zone != TT_GMT)
776 : {
777 : /* GMT+0300 is legal, but PST+0300 is not. */
778 0 : rest++;
779 0 : break;
780 : }
781 :
782 0 : sign = ((*rest == '+') ? 1 : -1);
783 0 : rest++; /* move over sign */
784 0 : end = rest;
785 0 : while (*end >= '0' && *end <= '9')
786 0 : end++;
787 0 : if (rest == end) /* no digits here */
788 0 : break;
789 :
790 0 : if ((end - rest) == 4)
791 : /* offset in HHMM */
792 0 : zone_offset = (((((rest[0]-'0')*10) + (rest[1]-'0')) * 60) +
793 0 : (((rest[2]-'0')*10) + (rest[3]-'0')));
794 0 : else if ((end - rest) == 2)
795 : /* offset in hours */
796 0 : zone_offset = (((rest[0]-'0')*10) + (rest[1]-'0')) * 60;
797 0 : else if ((end - rest) == 1)
798 : /* offset in hours */
799 0 : zone_offset = (rest[0]-'0') * 60;
800 : else
801 : /* 3 or >4 */
802 0 : break;
803 :
804 0 : zone_offset *= sign;
805 0 : zone = TT_GMT;
806 0 : break;
807 : }
808 :
809 : case '0': case '1': case '2': case '3': case '4':
810 : case '5': case '6': case '7': case '8': case '9':
811 : {
812 0 : int tmp_hour = -1;
813 0 : int tmp_min = -1;
814 0 : int tmp_sec = -1;
815 0 : const char *end = rest + 1;
816 0 : while (*end >= '0' && *end <= '9')
817 0 : end++;
818 :
819 : /* end is now the first character after a range of digits. */
820 :
821 0 : if (*end == ':')
822 : {
823 0 : if (hour >= 0 && min >= 0) /* already got it */
824 0 : break;
825 :
826 : /* We have seen "[0-9]+:", so this is probably HH:MM[:SS] */
827 0 : if ((end - rest) > 2)
828 : /* it is [0-9][0-9][0-9]+: */
829 0 : break;
830 0 : else if ((end - rest) == 2)
831 0 : tmp_hour = ((rest[0]-'0')*10 +
832 0 : (rest[1]-'0'));
833 : else
834 0 : tmp_hour = (rest[0]-'0');
835 :
836 : /* move over the colon, and parse minutes */
837 :
838 0 : rest = ++end;
839 0 : while (*end >= '0' && *end <= '9')
840 0 : end++;
841 :
842 0 : if (end == rest)
843 : /* no digits after first colon? */
844 0 : break;
845 0 : else if ((end - rest) > 2)
846 : /* it is [0-9][0-9][0-9]+: */
847 0 : break;
848 0 : else if ((end - rest) == 2)
849 0 : tmp_min = ((rest[0]-'0')*10 +
850 0 : (rest[1]-'0'));
851 : else
852 0 : tmp_min = (rest[0]-'0');
853 :
854 : /* now go for seconds */
855 0 : rest = end;
856 0 : if (*rest == ':')
857 0 : rest++;
858 0 : end = rest;
859 0 : while (*end >= '0' && *end <= '9')
860 0 : end++;
861 :
862 0 : if (end == rest)
863 : /* no digits after second colon - that's ok. */
864 : ;
865 0 : else if ((end - rest) > 2)
866 : /* it is [0-9][0-9][0-9]+: */
867 0 : break;
868 0 : else if ((end - rest) == 2)
869 0 : tmp_sec = ((rest[0]-'0')*10 +
870 0 : (rest[1]-'0'));
871 : else
872 0 : tmp_sec = (rest[0]-'0');
873 :
874 : /* If we made it here, we've parsed hour and min,
875 : and possibly sec, so it worked as a unit. */
876 :
877 : /* skip over whitespace and see if there's an AM or PM
878 : directly following the time.
879 : */
880 0 : if (tmp_hour <= 12)
881 : {
882 0 : const char *s = end;
883 0 : while (*s && (*s == ' ' || *s == '\t'))
884 0 : s++;
885 0 : if ((s[0] == 'p' || s[0] == 'P') &&
886 0 : (s[1] == 'm' || s[1] == 'M'))
887 : /* 10:05pm == 22:05, and 12:05pm == 12:05 */
888 0 : tmp_hour = (tmp_hour == 12 ? 12 : tmp_hour + 12);
889 0 : else if (tmp_hour == 12 &&
890 0 : (s[0] == 'a' || s[0] == 'A') &&
891 0 : (s[1] == 'm' || s[1] == 'M'))
892 : /* 12:05am == 00:05 */
893 0 : tmp_hour = 0;
894 : }
895 :
896 0 : hour = tmp_hour;
897 0 : min = tmp_min;
898 0 : sec = tmp_sec;
899 0 : rest = end;
900 0 : break;
901 : }
902 0 : else if ((*end == '/' || *end == '-') &&
903 0 : end[1] >= '0' && end[1] <= '9')
904 : {
905 : /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95
906 : or even 95-06-05...
907 : #### But it doesn't handle 1995-06-22.
908 : */
909 : int n1, n2, n3;
910 : const char *s;
911 :
912 0 : if (month != TT_UNKNOWN)
913 : /* if we saw a month name, this can't be. */
914 0 : break;
915 :
916 0 : s = rest;
917 :
918 0 : n1 = (*s++ - '0'); /* first 1 or 2 digits */
919 0 : if (*s >= '0' && *s <= '9')
920 0 : n1 = n1*10 + (*s++ - '0');
921 :
922 0 : if (*s != '/' && *s != '-') /* slash */
923 0 : break;
924 0 : s++;
925 :
926 0 : if (*s < '0' || *s > '9') /* second 1 or 2 digits */
927 0 : break;
928 0 : n2 = (*s++ - '0');
929 0 : if (*s >= '0' && *s <= '9')
930 0 : n2 = n2*10 + (*s++ - '0');
931 :
932 0 : if (*s != '/' && *s != '-') /* slash */
933 0 : break;
934 0 : s++;
935 :
936 0 : if (*s < '0' || *s > '9') /* third 1, 2, 4, or 5 digits */
937 0 : break;
938 0 : n3 = (*s++ - '0');
939 0 : if (*s >= '0' && *s <= '9')
940 0 : n3 = n3*10 + (*s++ - '0');
941 :
942 0 : if (*s >= '0' && *s <= '9') /* optional digits 3, 4, and 5 */
943 : {
944 0 : n3 = n3*10 + (*s++ - '0');
945 0 : if (*s < '0' || *s > '9')
946 0 : break;
947 0 : n3 = n3*10 + (*s++ - '0');
948 0 : if (*s >= '0' && *s <= '9')
949 0 : n3 = n3*10 + (*s++ - '0');
950 : }
951 :
952 0 : if ((*s >= '0' && *s <= '9') || /* followed by non-alphanum */
953 : (*s >= 'A' && *s <= 'Z') ||
954 : (*s >= 'a' && *s <= 'z'))
955 0 : break;
956 :
957 : /* Ok, we parsed three 1-2 digit numbers, with / or -
958 : between them. Now decide what the hell they are
959 : (DD/MM/YY or MM/DD/YY or YY/MM/DD.)
960 : */
961 :
962 0 : if (n1 > 31 || n1 == 0) /* must be YY/MM/DD */
963 : {
964 0 : if (n2 > 12) break;
965 0 : if (n3 > 31) break;
966 0 : year = n1;
967 0 : if (year < 70)
968 0 : year += 2000;
969 0 : else if (year < 100)
970 0 : year += 1900;
971 0 : month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
972 0 : date = n3;
973 0 : rest = s;
974 0 : break;
975 : }
976 :
977 0 : if (n1 > 12 && n2 > 12) /* illegal */
978 : {
979 0 : rest = s;
980 0 : break;
981 : }
982 :
983 0 : if (n3 < 70)
984 0 : n3 += 2000;
985 0 : else if (n3 < 100)
986 0 : n3 += 1900;
987 :
988 0 : if (n1 > 12) /* must be DD/MM/YY */
989 : {
990 0 : date = n1;
991 0 : month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
992 0 : year = n3;
993 : }
994 : else /* assume MM/DD/YY */
995 : {
996 : /* #### In the ambiguous case, should we consult the
997 : locale to find out the local default? */
998 0 : month = (TIME_TOKEN)(n1 + ((int)TT_JAN) - 1);
999 0 : date = n2;
1000 0 : year = n3;
1001 : }
1002 0 : rest = s;
1003 : }
1004 0 : else if ((*end >= 'A' && *end <= 'Z') ||
1005 : (*end >= 'a' && *end <= 'z'))
1006 : /* Digits followed by non-punctuation - what's that? */
1007 : ;
1008 0 : else if ((end - rest) == 5) /* five digits is a year */
1009 : year = (year < 0
1010 0 : ? ((rest[0]-'0')*10000L +
1011 0 : (rest[1]-'0')*1000L +
1012 0 : (rest[2]-'0')*100L +
1013 0 : (rest[3]-'0')*10L +
1014 0 : (rest[4]-'0'))
1015 0 : : year);
1016 0 : else if ((end - rest) == 4) /* four digits is a year */
1017 : year = (year < 0
1018 0 : ? ((rest[0]-'0')*1000L +
1019 0 : (rest[1]-'0')*100L +
1020 0 : (rest[2]-'0')*10L +
1021 0 : (rest[3]-'0'))
1022 0 : : year);
1023 0 : else if ((end - rest) == 2) /* two digits - date or year */
1024 : {
1025 0 : int n = ((rest[0]-'0')*10 +
1026 0 : (rest[1]-'0'));
1027 : /* If we don't have a date (day of the month) and we see a number
1028 : less than 32, then assume that is the date.
1029 :
1030 : Otherwise, if we have a date and not a year, assume this is the
1031 : year. If it is less than 70, then assume it refers to the 21st
1032 : century. If it is two digits (>= 70), assume it refers to this
1033 : century. Otherwise, assume it refers to an unambiguous year.
1034 :
1035 : The world will surely end soon.
1036 : */
1037 0 : if (date < 0 && n < 32)
1038 0 : date = n;
1039 0 : else if (year < 0)
1040 : {
1041 0 : if (n < 70)
1042 0 : year = 2000 + n;
1043 0 : else if (n < 100)
1044 0 : year = 1900 + n;
1045 : else
1046 0 : year = n;
1047 : }
1048 : /* else what the hell is this. */
1049 : }
1050 0 : else if ((end - rest) == 1) /* one digit - date */
1051 0 : date = (date < 0 ? (rest[0]-'0') : date);
1052 : /* else, three or more than five digits - what's that? */
1053 :
1054 0 : break;
1055 : }
1056 : }
1057 :
1058 : /* Skip to the end of this token, whether we parsed it or not.
1059 : Tokens are delimited by whitespace, or ,;-/
1060 : But explicitly not :+-.
1061 : */
1062 0 : while (*rest &&
1063 : *rest != ' ' && *rest != '\t' &&
1064 : *rest != ',' && *rest != ';' &&
1065 : *rest != '-' && *rest != '+' &&
1066 : *rest != '/' &&
1067 : *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']')
1068 0 : rest++;
1069 : /* skip over uninteresting chars. */
1070 : SKIP_MORE:
1071 0 : while (*rest &&
1072 : (*rest == ' ' || *rest == '\t' ||
1073 : *rest == ',' || *rest == ';' || *rest == '/' ||
1074 : *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']'))
1075 0 : rest++;
1076 :
1077 : /* "-" is ignored at the beginning of a token if we have not yet
1078 : parsed a year (e.g., the second "-" in "30-AUG-1966"), or if
1079 : the character after the dash is not a digit. */
1080 0 : if (*rest == '-' && ((rest > string && isalpha(rest[-1]) && year < 0)
1081 0 : || rest[1] < '0' || rest[1] > '9'))
1082 : {
1083 0 : rest++;
1084 0 : goto SKIP_MORE;
1085 : }
1086 :
1087 : }
1088 :
1089 0 : if (zone != TT_UNKNOWN && zone_offset == -1)
1090 : {
1091 0 : switch (zone)
1092 : {
1093 0 : case TT_PST: zone_offset = -8 * 60; break;
1094 0 : case TT_PDT: zone_offset = -8 * 60; dst_offset = 1 * 60; break;
1095 0 : case TT_MST: zone_offset = -7 * 60; break;
1096 0 : case TT_MDT: zone_offset = -7 * 60; dst_offset = 1 * 60; break;
1097 0 : case TT_CST: zone_offset = -6 * 60; break;
1098 0 : case TT_CDT: zone_offset = -6 * 60; dst_offset = 1 * 60; break;
1099 0 : case TT_EST: zone_offset = -5 * 60; break;
1100 0 : case TT_EDT: zone_offset = -5 * 60; dst_offset = 1 * 60; break;
1101 0 : case TT_AST: zone_offset = -4 * 60; break;
1102 0 : case TT_NST: zone_offset = -3 * 60 - 30; break;
1103 0 : case TT_GMT: zone_offset = 0 * 60; break;
1104 0 : case TT_BST: zone_offset = 0 * 60; dst_offset = 1 * 60; break;
1105 0 : case TT_MET: zone_offset = 1 * 60; break;
1106 0 : case TT_EET: zone_offset = 2 * 60; break;
1107 0 : case TT_JST: zone_offset = 9 * 60; break;
1108 : default:
1109 0 : PR_ASSERT (0);
1110 0 : break;
1111 : }
1112 : }
1113 :
1114 : /* If we didn't find a year, month, or day-of-the-month, we can't
1115 : possibly parse this, and in fact, mktime() will do something random
1116 : (I'm seeing it return "Tue Feb 5 06:28:16 2036", which is no doubt
1117 : a numerologically significant date... */
1118 0 : if (month == TT_UNKNOWN || date == -1 || year == -1 || year > PR_INT16_MAX)
1119 0 : return PR_FAILURE;
1120 :
1121 0 : memset(result, 0, sizeof(*result));
1122 0 : if (sec != -1)
1123 0 : result->tm_sec = sec;
1124 0 : if (min != -1)
1125 0 : result->tm_min = min;
1126 0 : if (hour != -1)
1127 0 : result->tm_hour = hour;
1128 0 : if (date != -1)
1129 0 : result->tm_mday = date;
1130 0 : if (month != TT_UNKNOWN)
1131 0 : result->tm_month = (((int)month) - ((int)TT_JAN));
1132 0 : if (year != -1)
1133 0 : result->tm_year = year;
1134 0 : if (dotw != TT_UNKNOWN)
1135 0 : result->tm_wday = (((int)dotw) - ((int)TT_SUN));
1136 : /*
1137 : * Mainly to compute wday and yday, but normalized time is also required
1138 : * by the check below that works around a Visual C++ 2005 mktime problem.
1139 : */
1140 0 : PR_NormalizeTime(result, PR_GMTParameters);
1141 : /* The remaining work is to set the gmt and dst offsets in tm_params. */
1142 :
1143 0 : if (zone == TT_UNKNOWN && default_to_gmt)
1144 : {
1145 : /* No zone was specified, so pretend the zone was GMT. */
1146 0 : zone = TT_GMT;
1147 0 : zone_offset = 0;
1148 : }
1149 :
1150 0 : if (zone_offset == -1)
1151 : {
1152 : /* no zone was specified, and we're to assume that everything
1153 : is local. */
1154 : struct tm localTime;
1155 : time_t secs;
1156 :
1157 0 : PR_ASSERT(result->tm_month > -1 &&
1158 : result->tm_mday > 0 &&
1159 : result->tm_hour > -1 &&
1160 : result->tm_min > -1 &&
1161 0 : result->tm_sec > -1);
1162 :
1163 : /*
1164 : * To obtain time_t from a tm structure representing the local
1165 : * time, we call mktime(). However, we need to see if we are
1166 : * on 1-Jan-1970 or before. If we are, we can't call mktime()
1167 : * because mktime() will crash on win16. In that case, we
1168 : * calculate zone_offset based on the zone offset at
1169 : * 00:00:00, 2 Jan 1970 GMT, and subtract zone_offset from the
1170 : * date we are parsing to transform the date to GMT. We also
1171 : * do so if mktime() returns (time_t) -1 (time out of range).
1172 : */
1173 :
1174 : /* month, day, hours, mins and secs are always non-negative
1175 : so we dont need to worry about them. */
1176 0 : if(result->tm_year >= 1970)
1177 : {
1178 : PRInt64 usec_per_sec;
1179 :
1180 0 : localTime.tm_sec = result->tm_sec;
1181 0 : localTime.tm_min = result->tm_min;
1182 0 : localTime.tm_hour = result->tm_hour;
1183 0 : localTime.tm_mday = result->tm_mday;
1184 0 : localTime.tm_mon = result->tm_month;
1185 0 : localTime.tm_year = result->tm_year - 1900;
1186 : /* Set this to -1 to tell mktime "I don't care". If you set
1187 : it to 0 or 1, you are making assertions about whether the
1188 : date you are handing it is in daylight savings mode or not;
1189 : and if you're wrong, it will "fix" it for you. */
1190 0 : localTime.tm_isdst = -1;
1191 :
1192 : #if _MSC_VER == 1400 /* 1400 = Visual C++ 2005 (8.0) */
1193 : /*
1194 : * mktime will return (time_t) -1 if the input is a date
1195 : * after 23:59:59, December 31, 3000, US Pacific Time (not
1196 : * UTC as documented):
1197 : * http://msdn.microsoft.com/en-us/library/d1y53h2a(VS.80).aspx
1198 : * But if the year is 3001, mktime also invokes the invalid
1199 : * parameter handler, causing the application to crash. This
1200 : * problem has been reported in
1201 : * http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=266036.
1202 : * We avoid this crash by not calling mktime if the date is
1203 : * out of range. To use a simple test that works in any time
1204 : * zone, we consider year 3000 out of range as well. (See
1205 : * bug 480740.)
1206 : */
1207 : if (result->tm_year >= 3000) {
1208 : /* Emulate what mktime would have done. */
1209 : errno = EINVAL;
1210 : secs = (time_t) -1;
1211 : } else {
1212 : secs = mktime(&localTime);
1213 : }
1214 : #else
1215 0 : secs = mktime(&localTime);
1216 : #endif
1217 0 : if (secs != (time_t) -1)
1218 : {
1219 : PRTime usecs64;
1220 0 : LL_I2L(usecs64, secs);
1221 0 : LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
1222 0 : LL_MUL(usecs64, usecs64, usec_per_sec);
1223 0 : *result_imploded = usecs64;
1224 0 : return PR_SUCCESS;
1225 : }
1226 : }
1227 :
1228 : /* So mktime() can't handle this case. We assume the
1229 : zone_offset for the date we are parsing is the same as
1230 : the zone offset on 00:00:00 2 Jan 1970 GMT. */
1231 0 : secs = 86400;
1232 0 : localtime_r(&secs, &localTime);
1233 : zone_offset = localTime.tm_min
1234 : + 60 * localTime.tm_hour
1235 0 : + 1440 * (localTime.tm_mday - 2);
1236 : }
1237 :
1238 0 : result->tm_params.tp_gmt_offset = zone_offset * 60;
1239 0 : result->tm_params.tp_dst_offset = dst_offset * 60;
1240 :
1241 0 : *result_imploded = PR_ImplodeTime(result);
1242 0 : return PR_SUCCESS;
1243 : }
1244 :
1245 : } // namespace nspr
|