Coverage Report

Created: 2024-05-20 06:23

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