VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/nsprpub/pr/src/misc/prtime.c@ 101972

Last change on this file since 101972 was 101972, checked in by vboxsync, 16 months ago

libs/xpcom: Remove some hopefully not used code (localtime_r should be available on the platforms we care about), bugref:10545

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette