nsprpub/pr/src/misc/prtime.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/nsprpub/pr/src/misc/prtime.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2011 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +/*
    1.10 + * prtime.c --
    1.11 + *
    1.12 + *     NSPR date and time functions
    1.13 + *
    1.14 + */
    1.15 +
    1.16 +#include "prinit.h"
    1.17 +#include "prtime.h"
    1.18 +#include "prlock.h"
    1.19 +#include "prprf.h"
    1.20 +#include "prlog.h"
    1.21 +
    1.22 +#include <string.h>
    1.23 +#include <ctype.h>
    1.24 +#include <errno.h>  /* for EINVAL */
    1.25 +#include <time.h>
    1.26 +
    1.27 +/* 
    1.28 + * The COUNT_LEAPS macro counts the number of leap years passed by
    1.29 + * till the start of the given year Y.  At the start of the year 4
    1.30 + * A.D. the number of leap years passed by is 0, while at the start of
    1.31 + * the year 5 A.D. this count is 1. The number of years divisible by
    1.32 + * 100 but not divisible by 400 (the non-leap years) is deducted from
    1.33 + * the count to get the correct number of leap years.
    1.34 + *
    1.35 + * The COUNT_DAYS macro counts the number of days since 01/01/01 till the
    1.36 + * start of the given year Y. The number of days at the start of the year
    1.37 + * 1 is 0 while the number of days at the start of the year 2 is 365
    1.38 + * (which is ((2)-1) * 365) and so on. The reference point is 01/01/01
    1.39 + * midnight 00:00:00.
    1.40 + */
    1.41 +
    1.42 +#define COUNT_LEAPS(Y)   ( ((Y)-1)/4 - ((Y)-1)/100 + ((Y)-1)/400 )
    1.43 +#define COUNT_DAYS(Y)  ( ((Y)-1)*365 + COUNT_LEAPS(Y) )
    1.44 +#define DAYS_BETWEEN_YEARS(A, B)  (COUNT_DAYS(B) - COUNT_DAYS(A))
    1.45 +
    1.46 +/*
    1.47 + * Static variables used by functions in this file
    1.48 + */
    1.49 +
    1.50 +/*
    1.51 + * The following array contains the day of year for the last day of
    1.52 + * each month, where index 1 is January, and day 0 is January 1.
    1.53 + */
    1.54 +
    1.55 +static const int lastDayOfMonth[2][13] = {
    1.56 +    {-1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364},
    1.57 +    {-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}
    1.58 +};
    1.59 +
    1.60 +/*
    1.61 + * The number of days in a month
    1.62 + */
    1.63 +
    1.64 +static const PRInt8 nDays[2][12] = {
    1.65 +    {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
    1.66 +    {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
    1.67 +};
    1.68 +
    1.69 +/*
    1.70 + * Declarations for internal functions defined later in this file.
    1.71 + */
    1.72 +
    1.73 +static void        ComputeGMT(PRTime time, PRExplodedTime *gmt);
    1.74 +static int         IsLeapYear(PRInt16 year);
    1.75 +static void        ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset);
    1.76 +
    1.77 +/*
    1.78 + *------------------------------------------------------------------------
    1.79 + *
    1.80 + * ComputeGMT --
    1.81 + *
    1.82 + *     Caveats:
    1.83 + *     - we ignore leap seconds
    1.84 + *
    1.85 + *------------------------------------------------------------------------
    1.86 + */
    1.87 +
    1.88 +static void
    1.89 +ComputeGMT(PRTime time, PRExplodedTime *gmt)
    1.90 +{
    1.91 +    PRInt32 tmp, rem;
    1.92 +    PRInt32 numDays;
    1.93 +    PRInt64 numDays64, rem64;
    1.94 +    int isLeap;
    1.95 +    PRInt64 sec;
    1.96 +    PRInt64 usec;
    1.97 +    PRInt64 usecPerSec;
    1.98 +    PRInt64 secPerDay;
    1.99 +
   1.100 +    /*
   1.101 +     * We first do the usec, sec, min, hour thing so that we do not
   1.102 +     * have to do LL arithmetic.
   1.103 +     */
   1.104 +
   1.105 +    LL_I2L(usecPerSec, 1000000L);
   1.106 +    LL_DIV(sec, time, usecPerSec);
   1.107 +    LL_MOD(usec, time, usecPerSec);
   1.108 +    LL_L2I(gmt->tm_usec, usec);
   1.109 +    /* Correct for weird mod semantics so the remainder is always positive */
   1.110 +    if (gmt->tm_usec < 0) {
   1.111 +        PRInt64 one;
   1.112 +
   1.113 +        LL_I2L(one, 1L);
   1.114 +        LL_SUB(sec, sec, one);
   1.115 +        gmt->tm_usec += 1000000L;
   1.116 +    }
   1.117 +
   1.118 +    LL_I2L(secPerDay, 86400L);
   1.119 +    LL_DIV(numDays64, sec, secPerDay);
   1.120 +    LL_MOD(rem64, sec, secPerDay);
   1.121 +    /* We are sure both of these numbers can fit into PRInt32 */
   1.122 +    LL_L2I(numDays, numDays64);
   1.123 +    LL_L2I(rem, rem64);
   1.124 +    if (rem < 0) {
   1.125 +        numDays--;
   1.126 +        rem += 86400L;
   1.127 +    }
   1.128 +
   1.129 +    /* Compute day of week.  Epoch started on a Thursday. */
   1.130 +
   1.131 +    gmt->tm_wday = (numDays + 4) % 7;
   1.132 +    if (gmt->tm_wday < 0) {
   1.133 +        gmt->tm_wday += 7;
   1.134 +    }
   1.135 +
   1.136 +    /* Compute the time of day. */
   1.137 +
   1.138 +    gmt->tm_hour = rem / 3600;
   1.139 +    rem %= 3600;
   1.140 +    gmt->tm_min = rem / 60;
   1.141 +    gmt->tm_sec = rem % 60;
   1.142 +
   1.143 +    /*
   1.144 +     * Compute the year by finding the 400 year period, then working
   1.145 +     * down from there.
   1.146 +     *
   1.147 +     * Since numDays is originally the number of days since January 1, 1970,
   1.148 +     * we must change it to be the number of days from January 1, 0001.
   1.149 +     */
   1.150 +
   1.151 +    numDays += 719162;       /* 719162 = days from year 1 up to 1970 */
   1.152 +    tmp = numDays / 146097;  /* 146097 = days in 400 years */
   1.153 +    rem = numDays % 146097;
   1.154 +    gmt->tm_year = tmp * 400 + 1;
   1.155 +
   1.156 +    /* Compute the 100 year period. */
   1.157 +
   1.158 +    tmp = rem / 36524;    /* 36524 = days in 100 years */
   1.159 +    rem %= 36524;
   1.160 +    if (tmp == 4) {       /* the 400th year is a leap year */
   1.161 +        tmp = 3;
   1.162 +        rem = 36524;
   1.163 +    }
   1.164 +    gmt->tm_year += tmp * 100;
   1.165 +
   1.166 +    /* Compute the 4 year period. */
   1.167 +
   1.168 +    tmp = rem / 1461;     /* 1461 = days in 4 years */
   1.169 +    rem %= 1461;
   1.170 +    gmt->tm_year += tmp * 4;
   1.171 +
   1.172 +    /* Compute which year in the 4. */
   1.173 +
   1.174 +    tmp = rem / 365;
   1.175 +    rem %= 365;
   1.176 +    if (tmp == 4) {       /* the 4th year is a leap year */
   1.177 +        tmp = 3;
   1.178 +        rem = 365;
   1.179 +    }
   1.180 +
   1.181 +    gmt->tm_year += tmp;
   1.182 +    gmt->tm_yday = rem;
   1.183 +    isLeap = IsLeapYear(gmt->tm_year);
   1.184 +
   1.185 +    /* Compute the month and day of month. */
   1.186 +
   1.187 +    for (tmp = 1; lastDayOfMonth[isLeap][tmp] < gmt->tm_yday; tmp++) {
   1.188 +    }
   1.189 +    gmt->tm_month = --tmp;
   1.190 +    gmt->tm_mday = gmt->tm_yday - lastDayOfMonth[isLeap][tmp];
   1.191 +
   1.192 +    gmt->tm_params.tp_gmt_offset = 0;
   1.193 +    gmt->tm_params.tp_dst_offset = 0;
   1.194 +}
   1.195 +
   1.196 +
   1.197 +/*
   1.198 + *------------------------------------------------------------------------
   1.199 + *
   1.200 + * PR_ExplodeTime --
   1.201 + *
   1.202 + *     Cf. struct tm *gmtime(const time_t *tp) and
   1.203 + *         struct tm *localtime(const time_t *tp)
   1.204 + *
   1.205 + *------------------------------------------------------------------------
   1.206 + */
   1.207 +
   1.208 +PR_IMPLEMENT(void)
   1.209 +PR_ExplodeTime(
   1.210 +        PRTime usecs,
   1.211 +        PRTimeParamFn params,
   1.212 +        PRExplodedTime *exploded)
   1.213 +{
   1.214 +    ComputeGMT(usecs, exploded);
   1.215 +    exploded->tm_params = params(exploded);
   1.216 +    ApplySecOffset(exploded, exploded->tm_params.tp_gmt_offset
   1.217 +            + exploded->tm_params.tp_dst_offset);
   1.218 +}
   1.219 +
   1.220 +
   1.221 +/*
   1.222 + *------------------------------------------------------------------------
   1.223 + *
   1.224 + * PR_ImplodeTime --
   1.225 + *
   1.226 + *     Cf. time_t mktime(struct tm *tp)
   1.227 + *     Note that 1 year has < 2^25 seconds.  So an PRInt32 is large enough.
   1.228 + *
   1.229 + *------------------------------------------------------------------------
   1.230 + */
   1.231 +PR_IMPLEMENT(PRTime)
   1.232 +PR_ImplodeTime(const PRExplodedTime *exploded)
   1.233 +{
   1.234 +    PRExplodedTime copy;
   1.235 +    PRTime retVal;
   1.236 +    PRInt64 secPerDay, usecPerSec;
   1.237 +    PRInt64 temp;
   1.238 +    PRInt64 numSecs64;
   1.239 +    PRInt32 numDays;
   1.240 +    PRInt32 numSecs;
   1.241 +
   1.242 +    /* Normalize first.  Do this on our copy */
   1.243 +    copy = *exploded;
   1.244 +    PR_NormalizeTime(&copy, PR_GMTParameters);
   1.245 +
   1.246 +    numDays = DAYS_BETWEEN_YEARS(1970, copy.tm_year);
   1.247 +    
   1.248 +    numSecs = copy.tm_yday * 86400 + copy.tm_hour * 3600
   1.249 +            + copy.tm_min * 60 + copy.tm_sec;
   1.250 +
   1.251 +    LL_I2L(temp, numDays);
   1.252 +    LL_I2L(secPerDay, 86400);
   1.253 +    LL_MUL(temp, temp, secPerDay);
   1.254 +    LL_I2L(numSecs64, numSecs);
   1.255 +    LL_ADD(numSecs64, numSecs64, temp);
   1.256 +
   1.257 +    /* apply the GMT and DST offsets */
   1.258 +    LL_I2L(temp,  copy.tm_params.tp_gmt_offset);
   1.259 +    LL_SUB(numSecs64, numSecs64, temp);
   1.260 +    LL_I2L(temp,  copy.tm_params.tp_dst_offset);
   1.261 +    LL_SUB(numSecs64, numSecs64, temp);
   1.262 +
   1.263 +    LL_I2L(usecPerSec, 1000000L);
   1.264 +    LL_MUL(temp, numSecs64, usecPerSec);
   1.265 +    LL_I2L(retVal, copy.tm_usec);
   1.266 +    LL_ADD(retVal, retVal, temp);
   1.267 +
   1.268 +    return retVal;
   1.269 +}
   1.270 +
   1.271 +/*
   1.272 + *-------------------------------------------------------------------------
   1.273 + *
   1.274 + * IsLeapYear --
   1.275 + *
   1.276 + *     Returns 1 if the year is a leap year, 0 otherwise.
   1.277 + *
   1.278 + *-------------------------------------------------------------------------
   1.279 + */
   1.280 +
   1.281 +static int IsLeapYear(PRInt16 year)
   1.282 +{
   1.283 +    if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
   1.284 +        return 1;
   1.285 +    else
   1.286 +        return 0;
   1.287 +}
   1.288 +
   1.289 +/*
   1.290 + * 'secOffset' should be less than 86400 (i.e., a day).
   1.291 + * 'time' should point to a normalized PRExplodedTime.
   1.292 + */
   1.293 +
   1.294 +static void
   1.295 +ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset)
   1.296 +{
   1.297 +    time->tm_sec += secOffset;
   1.298 +
   1.299 +    /* Note that in this implementation we do not count leap seconds */
   1.300 +    if (time->tm_sec < 0 || time->tm_sec >= 60) {
   1.301 +        time->tm_min += time->tm_sec / 60;
   1.302 +        time->tm_sec %= 60;
   1.303 +        if (time->tm_sec < 0) {
   1.304 +            time->tm_sec += 60;
   1.305 +            time->tm_min--;
   1.306 +        }
   1.307 +    }
   1.308 +
   1.309 +    if (time->tm_min < 0 || time->tm_min >= 60) {
   1.310 +        time->tm_hour += time->tm_min / 60;
   1.311 +        time->tm_min %= 60;
   1.312 +        if (time->tm_min < 0) {
   1.313 +            time->tm_min += 60;
   1.314 +            time->tm_hour--;
   1.315 +        }
   1.316 +    }
   1.317 +
   1.318 +    if (time->tm_hour < 0) {
   1.319 +        /* Decrement mday, yday, and wday */
   1.320 +        time->tm_hour += 24;
   1.321 +        time->tm_mday--;
   1.322 +        time->tm_yday--;
   1.323 +        if (time->tm_mday < 1) {
   1.324 +            time->tm_month--;
   1.325 +            if (time->tm_month < 0) {
   1.326 +                time->tm_month = 11;
   1.327 +                time->tm_year--;
   1.328 +                if (IsLeapYear(time->tm_year))
   1.329 +                    time->tm_yday = 365;
   1.330 +                else
   1.331 +                    time->tm_yday = 364;
   1.332 +            }
   1.333 +            time->tm_mday = nDays[IsLeapYear(time->tm_year)][time->tm_month];
   1.334 +        }
   1.335 +        time->tm_wday--;
   1.336 +        if (time->tm_wday < 0)
   1.337 +            time->tm_wday = 6;
   1.338 +    } else if (time->tm_hour > 23) {
   1.339 +        /* Increment mday, yday, and wday */
   1.340 +        time->tm_hour -= 24;
   1.341 +        time->tm_mday++;
   1.342 +        time->tm_yday++;
   1.343 +        if (time->tm_mday >
   1.344 +                nDays[IsLeapYear(time->tm_year)][time->tm_month]) {
   1.345 +            time->tm_mday = 1;
   1.346 +            time->tm_month++;
   1.347 +            if (time->tm_month > 11) {
   1.348 +                time->tm_month = 0;
   1.349 +                time->tm_year++;
   1.350 +                time->tm_yday = 0;
   1.351 +            }
   1.352 +        }
   1.353 +        time->tm_wday++;
   1.354 +        if (time->tm_wday > 6)
   1.355 +            time->tm_wday = 0;
   1.356 +    }
   1.357 +}
   1.358 +
   1.359 +PR_IMPLEMENT(void)
   1.360 +PR_NormalizeTime(PRExplodedTime *time, PRTimeParamFn params)
   1.361 +{
   1.362 +    int daysInMonth;
   1.363 +    PRInt32 numDays;
   1.364 +
   1.365 +    /* Get back to GMT */
   1.366 +    time->tm_sec -= time->tm_params.tp_gmt_offset
   1.367 +            + time->tm_params.tp_dst_offset;
   1.368 +    time->tm_params.tp_gmt_offset = 0;
   1.369 +    time->tm_params.tp_dst_offset = 0;
   1.370 +
   1.371 +    /* Now normalize GMT */
   1.372 +
   1.373 +    if (time->tm_usec < 0 || time->tm_usec >= 1000000) {
   1.374 +        time->tm_sec +=  time->tm_usec / 1000000;
   1.375 +        time->tm_usec %= 1000000;
   1.376 +        if (time->tm_usec < 0) {
   1.377 +            time->tm_usec += 1000000;
   1.378 +            time->tm_sec--;
   1.379 +        }
   1.380 +    }
   1.381 +
   1.382 +    /* Note that we do not count leap seconds in this implementation */
   1.383 +    if (time->tm_sec < 0 || time->tm_sec >= 60) {
   1.384 +        time->tm_min += time->tm_sec / 60;
   1.385 +        time->tm_sec %= 60;
   1.386 +        if (time->tm_sec < 0) {
   1.387 +            time->tm_sec += 60;
   1.388 +            time->tm_min--;
   1.389 +        }
   1.390 +    }
   1.391 +
   1.392 +    if (time->tm_min < 0 || time->tm_min >= 60) {
   1.393 +        time->tm_hour += time->tm_min / 60;
   1.394 +        time->tm_min %= 60;
   1.395 +        if (time->tm_min < 0) {
   1.396 +            time->tm_min += 60;
   1.397 +            time->tm_hour--;
   1.398 +        }
   1.399 +    }
   1.400 +
   1.401 +    if (time->tm_hour < 0 || time->tm_hour >= 24) {
   1.402 +        time->tm_mday += time->tm_hour / 24;
   1.403 +        time->tm_hour %= 24;
   1.404 +        if (time->tm_hour < 0) {
   1.405 +            time->tm_hour += 24;
   1.406 +            time->tm_mday--;
   1.407 +        }
   1.408 +    }
   1.409 +
   1.410 +    /* Normalize month and year before mday */
   1.411 +    if (time->tm_month < 0 || time->tm_month >= 12) {
   1.412 +        time->tm_year += time->tm_month / 12;
   1.413 +        time->tm_month %= 12;
   1.414 +        if (time->tm_month < 0) {
   1.415 +            time->tm_month += 12;
   1.416 +            time->tm_year--;
   1.417 +        }
   1.418 +    }
   1.419 +
   1.420 +    /* Now that month and year are in proper range, normalize mday */
   1.421 +
   1.422 +    if (time->tm_mday < 1) {
   1.423 +        /* mday too small */
   1.424 +        do {
   1.425 +            /* the previous month */
   1.426 +            time->tm_month--;
   1.427 +            if (time->tm_month < 0) {
   1.428 +                time->tm_month = 11;
   1.429 +                time->tm_year--;
   1.430 +            }
   1.431 +            time->tm_mday += nDays[IsLeapYear(time->tm_year)][time->tm_month];
   1.432 +        } while (time->tm_mday < 1);
   1.433 +    } else {
   1.434 +        daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month];
   1.435 +        while (time->tm_mday > daysInMonth) {
   1.436 +            /* mday too large */
   1.437 +            time->tm_mday -= daysInMonth;
   1.438 +            time->tm_month++;
   1.439 +            if (time->tm_month > 11) {
   1.440 +                time->tm_month = 0;
   1.441 +                time->tm_year++;
   1.442 +            }
   1.443 +            daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month];
   1.444 +        }
   1.445 +    }
   1.446 +
   1.447 +    /* Recompute yday and wday */
   1.448 +    time->tm_yday = time->tm_mday +
   1.449 +            lastDayOfMonth[IsLeapYear(time->tm_year)][time->tm_month];
   1.450 +	    
   1.451 +    numDays = DAYS_BETWEEN_YEARS(1970, time->tm_year) + time->tm_yday;
   1.452 +    time->tm_wday = (numDays + 4) % 7;
   1.453 +    if (time->tm_wday < 0) {
   1.454 +        time->tm_wday += 7;
   1.455 +    }
   1.456 +
   1.457 +    /* Recompute time parameters */
   1.458 +
   1.459 +    time->tm_params = params(time);
   1.460 +
   1.461 +    ApplySecOffset(time, time->tm_params.tp_gmt_offset
   1.462 +            + time->tm_params.tp_dst_offset);
   1.463 +}
   1.464 +
   1.465 +
   1.466 +/*
   1.467 + *-------------------------------------------------------------------------
   1.468 + *
   1.469 + * PR_LocalTimeParameters --
   1.470 + * 
   1.471 + *     returns the time parameters for the local time zone
   1.472 + *
   1.473 + *     The following uses localtime() from the standard C library.
   1.474 + *     (time.h)  This is our fallback implementation.  Unix, PC, and BeOS
   1.475 + *     use this version.  A platform may have its own machine-dependent
   1.476 + *     implementation of this function.
   1.477 + *
   1.478 + *-------------------------------------------------------------------------
   1.479 + */
   1.480 +
   1.481 +#if defined(HAVE_INT_LOCALTIME_R)
   1.482 +
   1.483 +/*
   1.484 + * In this case we could define the macro as
   1.485 + *     #define MT_safe_localtime(timer, result) \
   1.486 + *             (localtime_r(timer, result) == 0 ? result : NULL)
   1.487 + * I chose to compare the return value of localtime_r with -1 so 
   1.488 + * that I can catch the cases where localtime_r returns a pointer
   1.489 + * to struct tm.  The macro definition above would not be able to
   1.490 + * detect such mistakes because it is legal to compare a pointer
   1.491 + * with 0.
   1.492 + */
   1.493 +
   1.494 +#define MT_safe_localtime(timer, result) \
   1.495 +        (localtime_r(timer, result) == -1 ? NULL: result)
   1.496 +
   1.497 +#elif defined(HAVE_POINTER_LOCALTIME_R)
   1.498 +
   1.499 +#define MT_safe_localtime localtime_r
   1.500 +
   1.501 +#else
   1.502 +
   1.503 +#define HAVE_LOCALTIME_MONITOR 1  /* We use 'monitor' to serialize our calls
   1.504 +                                   * to localtime(). */
   1.505 +static PRLock *monitor = NULL;
   1.506 +
   1.507 +static struct tm *MT_safe_localtime(const time_t *clock, struct tm *result)
   1.508 +{
   1.509 +    struct tm *tmPtr;
   1.510 +    int needLock = PR_Initialized();  /* We need to use a lock to protect
   1.511 +                                       * against NSPR threads only when the
   1.512 +                                       * NSPR thread system is activated. */
   1.513 +
   1.514 +    if (needLock) PR_Lock(monitor);
   1.515 +
   1.516 +    /*
   1.517 +     * Microsoft (all flavors) localtime() returns a NULL pointer if 'clock'
   1.518 +     * represents a time before midnight January 1, 1970.  In
   1.519 +     * that case, we also return a NULL pointer and the struct tm
   1.520 +     * object pointed to by 'result' is not modified.
   1.521 +     *
   1.522 +     * Watcom C/C++ 11.0 localtime() treats time_t as unsigned long
   1.523 +     * hence, does not recognize negative values of clock as pre-1/1/70.
   1.524 +     * We have to manually check (WIN16 only) for negative value of
   1.525 +     * clock and return NULL.
   1.526 +     *
   1.527 +     * With negative values of clock, OS/2 returns the struct tm for
   1.528 +     * clock plus ULONG_MAX. So we also have to check for the invalid
   1.529 +     * structs returned for timezones west of Greenwich when clock == 0.
   1.530 +     */
   1.531 +    
   1.532 +    tmPtr = localtime(clock);
   1.533 +
   1.534 +#if defined(WIN16) || defined(XP_OS2)
   1.535 +    if ( (PRInt32) *clock < 0 ||
   1.536 +         ( (PRInt32) *clock == 0 && tmPtr->tm_year != 70))
   1.537 +        result = NULL;
   1.538 +    else
   1.539 +        *result = *tmPtr;
   1.540 +#else
   1.541 +    if (tmPtr) {
   1.542 +        *result = *tmPtr;
   1.543 +    } else {
   1.544 +        result = NULL;
   1.545 +    }
   1.546 +#endif /* WIN16 */
   1.547 +
   1.548 +    if (needLock) PR_Unlock(monitor);
   1.549 +
   1.550 +    return result;
   1.551 +}
   1.552 +
   1.553 +#endif  /* definition of MT_safe_localtime() */
   1.554 +
   1.555 +void _PR_InitTime(void)
   1.556 +{
   1.557 +#ifdef HAVE_LOCALTIME_MONITOR
   1.558 +    monitor = PR_NewLock();
   1.559 +#endif
   1.560 +#ifdef WINCE
   1.561 +    _MD_InitTime();
   1.562 +#endif
   1.563 +}
   1.564 +
   1.565 +void _PR_CleanupTime(void)
   1.566 +{
   1.567 +#ifdef HAVE_LOCALTIME_MONITOR
   1.568 +    if (monitor) {
   1.569 +        PR_DestroyLock(monitor);
   1.570 +        monitor = NULL;
   1.571 +    }
   1.572 +#endif
   1.573 +#ifdef WINCE
   1.574 +    _MD_CleanupTime();
   1.575 +#endif
   1.576 +}
   1.577 +
   1.578 +#if defined(XP_UNIX) || defined(XP_PC) || defined(XP_BEOS)
   1.579 +
   1.580 +PR_IMPLEMENT(PRTimeParameters)
   1.581 +PR_LocalTimeParameters(const PRExplodedTime *gmt)
   1.582 +{
   1.583 +
   1.584 +    PRTimeParameters retVal;
   1.585 +    struct tm localTime;
   1.586 +    time_t secs;
   1.587 +    PRTime secs64;
   1.588 +    PRInt64 usecPerSec;
   1.589 +    PRInt64 usecPerSec_1;
   1.590 +    PRInt64 maxInt32;
   1.591 +    PRInt64 minInt32;
   1.592 +    PRInt32 dayOffset;
   1.593 +    PRInt32 offset2Jan1970;
   1.594 +    PRInt32 offsetNew;
   1.595 +    int isdst2Jan1970;
   1.596 +
   1.597 +    /*
   1.598 +     * Calculate the GMT offset.  First, figure out what is
   1.599 +     * 00:00:00 Jan. 2, 1970 GMT (which is exactly a day, or 86400
   1.600 +     * seconds, since the epoch) in local time.  Then we calculate
   1.601 +     * the difference between local time and GMT in seconds:
   1.602 +     *     gmt_offset = local_time - GMT
   1.603 +     *
   1.604 +     * Caveat: the validity of this calculation depends on two
   1.605 +     * assumptions:
   1.606 +     * 1. Daylight saving time was not in effect on Jan. 2, 1970.
   1.607 +     * 2. The time zone of the geographic location has not changed
   1.608 +     *    since Jan. 2, 1970.
   1.609 +     */
   1.610 +
   1.611 +    secs = 86400L;
   1.612 +    (void) MT_safe_localtime(&secs, &localTime);
   1.613 +
   1.614 +    /* GMT is 00:00:00, 2nd of Jan. */
   1.615 +
   1.616 +    offset2Jan1970 = (PRInt32)localTime.tm_sec 
   1.617 +            + 60L * (PRInt32)localTime.tm_min
   1.618 +            + 3600L * (PRInt32)localTime.tm_hour
   1.619 +            + 86400L * (PRInt32)((PRInt32)localTime.tm_mday - 2L);
   1.620 +
   1.621 +    isdst2Jan1970 = localTime.tm_isdst;
   1.622 +
   1.623 +    /*
   1.624 +     * Now compute DST offset.  We calculate the overall offset
   1.625 +     * of local time from GMT, similar to above.  The overall
   1.626 +     * offset has two components: gmt offset and dst offset.
   1.627 +     * We subtract gmt offset from the overall offset to get
   1.628 +     * the dst offset.
   1.629 +     *     overall_offset = local_time - GMT
   1.630 +     *     overall_offset = gmt_offset + dst_offset
   1.631 +     * ==> dst_offset = local_time - GMT - gmt_offset
   1.632 +     */
   1.633 +
   1.634 +    secs64 = PR_ImplodeTime(gmt);    /* This is still in microseconds */
   1.635 +    LL_I2L(usecPerSec, PR_USEC_PER_SEC);
   1.636 +    LL_I2L(usecPerSec_1, PR_USEC_PER_SEC - 1);
   1.637 +    /* Convert to seconds, truncating down (3.1 -> 3 and -3.1 -> -4) */
   1.638 +    if (LL_GE_ZERO(secs64)) {
   1.639 +        LL_DIV(secs64, secs64, usecPerSec);
   1.640 +    } else {
   1.641 +        LL_NEG(secs64, secs64);
   1.642 +        LL_ADD(secs64, secs64, usecPerSec_1);
   1.643 +        LL_DIV(secs64, secs64, usecPerSec);
   1.644 +        LL_NEG(secs64, secs64);
   1.645 +    }
   1.646 +    LL_I2L(maxInt32, PR_INT32_MAX);
   1.647 +    LL_I2L(minInt32, PR_INT32_MIN);
   1.648 +    if (LL_CMP(secs64, >, maxInt32) || LL_CMP(secs64, <, minInt32)) {
   1.649 +        /* secs64 is too large or too small for time_t (32-bit integer) */
   1.650 +        retVal.tp_gmt_offset = offset2Jan1970;
   1.651 +        retVal.tp_dst_offset = 0;
   1.652 +        return retVal;
   1.653 +    }
   1.654 +    LL_L2I(secs, secs64);
   1.655 +
   1.656 +    /*
   1.657 +     * On Windows, localtime() (and our MT_safe_localtime() too)
   1.658 +     * returns a NULL pointer for time before midnight January 1,
   1.659 +     * 1970 GMT.  In that case, we just use the GMT offset for
   1.660 +     * Jan 2, 1970 and assume that DST was not in effect.
   1.661 +     */
   1.662 +
   1.663 +    if (MT_safe_localtime(&secs, &localTime) == NULL) {
   1.664 +        retVal.tp_gmt_offset = offset2Jan1970;
   1.665 +        retVal.tp_dst_offset = 0;
   1.666 +        return retVal;
   1.667 +    }
   1.668 +
   1.669 +    /*
   1.670 +     * dayOffset is the offset between local time and GMT in 
   1.671 +     * the day component, which can only be -1, 0, or 1.  We
   1.672 +     * use the day of the week to compute dayOffset.
   1.673 +     */
   1.674 +
   1.675 +    dayOffset = (PRInt32) localTime.tm_wday - gmt->tm_wday;
   1.676 +
   1.677 +    /*
   1.678 +     * Need to adjust for wrapping around of day of the week from
   1.679 +     * 6 back to 0.
   1.680 +     */
   1.681 +
   1.682 +    if (dayOffset == -6) {
   1.683 +        /* Local time is Sunday (0) and GMT is Saturday (6) */
   1.684 +        dayOffset = 1;
   1.685 +    } else if (dayOffset == 6) {
   1.686 +        /* Local time is Saturday (6) and GMT is Sunday (0) */
   1.687 +        dayOffset = -1;
   1.688 +    }
   1.689 +
   1.690 +    offsetNew = (PRInt32)localTime.tm_sec - gmt->tm_sec
   1.691 +            + 60L * ((PRInt32)localTime.tm_min - gmt->tm_min)
   1.692 +            + 3600L * ((PRInt32)localTime.tm_hour - gmt->tm_hour)
   1.693 +            + 86400L * (PRInt32)dayOffset;
   1.694 +
   1.695 +    if (localTime.tm_isdst <= 0) {
   1.696 +        /* DST is not in effect */
   1.697 +        retVal.tp_gmt_offset = offsetNew;
   1.698 +        retVal.tp_dst_offset = 0;
   1.699 +    } else {
   1.700 +        /* DST is in effect */
   1.701 +        if (isdst2Jan1970 <=0) {
   1.702 +            /*
   1.703 +             * DST was not in effect back in 2 Jan. 1970.
   1.704 +             * Use the offset back then as the GMT offset,
   1.705 +             * assuming the time zone has not changed since then.
   1.706 +             */
   1.707 +            retVal.tp_gmt_offset = offset2Jan1970;
   1.708 +            retVal.tp_dst_offset = offsetNew - offset2Jan1970;
   1.709 +        } else {
   1.710 +            /*
   1.711 +             * DST was also in effect back in 2 Jan. 1970.
   1.712 +             * Then our clever trick (or rather, ugly hack) fails.
   1.713 +             * We will just assume DST offset is an hour.
   1.714 +             */
   1.715 +            retVal.tp_gmt_offset = offsetNew - 3600;
   1.716 +            retVal.tp_dst_offset = 3600;
   1.717 +        }
   1.718 +    }
   1.719 +    
   1.720 +    return retVal;
   1.721 +}
   1.722 +
   1.723 +#endif    /* defined(XP_UNIX) || defined(XP_PC) || defined(XP_BEOS) */
   1.724 +
   1.725 +/*
   1.726 + *------------------------------------------------------------------------
   1.727 + *
   1.728 + * PR_USPacificTimeParameters --
   1.729 + *
   1.730 + *     The time parameters function for the US Pacific Time Zone.
   1.731 + *
   1.732 + *------------------------------------------------------------------------
   1.733 + */
   1.734 +
   1.735 +/*
   1.736 + * Returns the mday of the first sunday of the month, where
   1.737 + * mday and wday are for a given day in the month.
   1.738 + * mdays start with 1 (e.g. 1..31).  
   1.739 + * wdays start with 0 and are in the range 0..6.  0 = Sunday.
   1.740 + */
   1.741 +#define firstSunday(mday, wday) (((mday - wday + 7 - 1) % 7) + 1)
   1.742 +
   1.743 +/*
   1.744 + * Returns the mday for the N'th Sunday of the month, where 
   1.745 + * mday and wday are for a given day in the month.
   1.746 + * mdays start with 1 (e.g. 1..31).  
   1.747 + * wdays start with 0 and are in the range 0..6.  0 = Sunday.
   1.748 + * N has the following values: 0 = first, 1 = second (etc), -1 = last.
   1.749 + * ndays is the number of days in that month, the same value as the 
   1.750 + * mday of the last day of the month.
   1.751 + */
   1.752 +static PRInt32 
   1.753 +NthSunday(PRInt32 mday, PRInt32 wday, PRInt32 N, PRInt32 ndays) 
   1.754 +{
   1.755 +    PRInt32 firstSun = firstSunday(mday, wday);
   1.756 +
   1.757 +    if (N < 0) 
   1.758 +        N = (ndays - firstSun) / 7;
   1.759 +    return firstSun + (7 * N);
   1.760 +}
   1.761 +
   1.762 +typedef struct DSTParams {
   1.763 +    PRInt8 dst_start_month;       /* 0 = January */
   1.764 +    PRInt8 dst_start_Nth_Sunday;  /* N as defined above */
   1.765 +    PRInt8 dst_start_month_ndays; /* ndays as defined above */
   1.766 +    PRInt8 dst_end_month;         /* 0 = January */
   1.767 +    PRInt8 dst_end_Nth_Sunday;    /* N as defined above */
   1.768 +    PRInt8 dst_end_month_ndays;   /* ndays as defined above */
   1.769 +} DSTParams;
   1.770 +
   1.771 +static const DSTParams dstParams[2] = {
   1.772 +    /* year < 2007:  First April Sunday - Last October Sunday */
   1.773 +    { 3, 0, 30, 9, -1, 31 },
   1.774 +    /* year >= 2007: Second March Sunday - First November Sunday */
   1.775 +    { 2, 1, 31, 10, 0, 30 }
   1.776 +};
   1.777 +
   1.778 +PR_IMPLEMENT(PRTimeParameters)
   1.779 +PR_USPacificTimeParameters(const PRExplodedTime *gmt)
   1.780 +{
   1.781 +    const DSTParams *dst;
   1.782 +    PRTimeParameters retVal;
   1.783 +    PRExplodedTime st;
   1.784 +
   1.785 +    /*
   1.786 +     * Based on geographic location and GMT, figure out offset of
   1.787 +     * standard time from GMT.  In this example implementation, we
   1.788 +     * assume the local time zone is US Pacific Time.
   1.789 +     */
   1.790 +
   1.791 +    retVal.tp_gmt_offset = -8L * 3600L;
   1.792 +
   1.793 +    /*
   1.794 +     * Make a copy of GMT.  Note that the tm_params field of this copy
   1.795 +     * is ignored.
   1.796 +     */
   1.797 +
   1.798 +    st.tm_usec = gmt->tm_usec;
   1.799 +    st.tm_sec = gmt->tm_sec;
   1.800 +    st.tm_min = gmt->tm_min;
   1.801 +    st.tm_hour = gmt->tm_hour;
   1.802 +    st.tm_mday = gmt->tm_mday;
   1.803 +    st.tm_month = gmt->tm_month;
   1.804 +    st.tm_year = gmt->tm_year;
   1.805 +    st.tm_wday = gmt->tm_wday;
   1.806 +    st.tm_yday = gmt->tm_yday;
   1.807 +
   1.808 +    /* Apply the offset to GMT to obtain the local standard time */
   1.809 +    ApplySecOffset(&st, retVal.tp_gmt_offset);
   1.810 +
   1.811 +    if (st.tm_year < 2007) { /* first April Sunday - Last October Sunday */
   1.812 +	dst = &dstParams[0];
   1.813 +    } else {                 /* Second March Sunday - First November Sunday */
   1.814 +	dst = &dstParams[1];
   1.815 +    }
   1.816 +
   1.817 +    /*
   1.818 +     * Apply the rules on standard time or GMT to obtain daylight saving
   1.819 +     * time offset.  In this implementation, we use the US DST rule.
   1.820 +     */
   1.821 +    if (st.tm_month < dst->dst_start_month) {
   1.822 +        retVal.tp_dst_offset = 0L;
   1.823 +    } else if (st.tm_month == dst->dst_start_month) {
   1.824 +	int NthSun = NthSunday(st.tm_mday, st.tm_wday, 
   1.825 +			       dst->dst_start_Nth_Sunday, 
   1.826 +			       dst->dst_start_month_ndays);
   1.827 +	if (st.tm_mday < NthSun) {              /* Before starting Sunday */
   1.828 +	    retVal.tp_dst_offset = 0L;
   1.829 +        } else if (st.tm_mday == NthSun) {      /* Starting Sunday */
   1.830 +	    /* 01:59:59 PST -> 03:00:00 PDT */
   1.831 +	    if (st.tm_hour < 2) {
   1.832 +		retVal.tp_dst_offset = 0L;
   1.833 +	    } else {
   1.834 +		retVal.tp_dst_offset = 3600L;
   1.835 +	    }
   1.836 +	} else {                                /* After starting Sunday */
   1.837 +	    retVal.tp_dst_offset = 3600L;
   1.838 +        }
   1.839 +    } else if (st.tm_month < dst->dst_end_month) {
   1.840 +        retVal.tp_dst_offset = 3600L;
   1.841 +    } else if (st.tm_month == dst->dst_end_month) {
   1.842 +	int NthSun = NthSunday(st.tm_mday, st.tm_wday, 
   1.843 +			       dst->dst_end_Nth_Sunday, 
   1.844 +			       dst->dst_end_month_ndays);
   1.845 +	if (st.tm_mday < NthSun) {              /* Before ending Sunday */
   1.846 +	    retVal.tp_dst_offset = 3600L;
   1.847 +        } else if (st.tm_mday == NthSun) {      /* Ending Sunday */
   1.848 +	    /* 01:59:59 PDT -> 01:00:00 PST */
   1.849 +	    if (st.tm_hour < 1) {
   1.850 +		retVal.tp_dst_offset = 3600L;
   1.851 +	    } else {
   1.852 +		retVal.tp_dst_offset = 0L;
   1.853 +	    }
   1.854 +	} else {                                /* After ending Sunday */
   1.855 +	    retVal.tp_dst_offset = 0L;
   1.856 +        }
   1.857 +    } else {
   1.858 +        retVal.tp_dst_offset = 0L;
   1.859 +    }
   1.860 +    return retVal;
   1.861 +}
   1.862 +
   1.863 +/*
   1.864 + *------------------------------------------------------------------------
   1.865 + *
   1.866 + * PR_GMTParameters --
   1.867 + *
   1.868 + *     Returns the PRTimeParameters for Greenwich Mean Time.
   1.869 + *     Trivially, both the tp_gmt_offset and tp_dst_offset fields are 0.
   1.870 + *
   1.871 + *------------------------------------------------------------------------
   1.872 + */
   1.873 +
   1.874 +PR_IMPLEMENT(PRTimeParameters)
   1.875 +PR_GMTParameters(const PRExplodedTime *gmt)
   1.876 +{
   1.877 +    PRTimeParameters retVal = { 0, 0 };
   1.878 +    return retVal;
   1.879 +}
   1.880 +
   1.881 +/*
   1.882 + * The following code implements PR_ParseTimeString().  It is based on
   1.883 + * ns/lib/xp/xp_time.c, revision 1.25, by Jamie Zawinski <jwz@netscape.com>.
   1.884 + */
   1.885 +
   1.886 +/*
   1.887 + * We only recognize the abbreviations of a small subset of time zones
   1.888 + * in North America, Europe, and Japan.
   1.889 + *
   1.890 + * PST/PDT: Pacific Standard/Daylight Time
   1.891 + * MST/MDT: Mountain Standard/Daylight Time
   1.892 + * CST/CDT: Central Standard/Daylight Time
   1.893 + * EST/EDT: Eastern Standard/Daylight Time
   1.894 + * AST: Atlantic Standard Time
   1.895 + * NST: Newfoundland Standard Time
   1.896 + * GMT: Greenwich Mean Time
   1.897 + * BST: British Summer Time
   1.898 + * MET: Middle Europe Time
   1.899 + * EET: Eastern Europe Time
   1.900 + * JST: Japan Standard Time
   1.901 + */
   1.902 +
   1.903 +typedef enum
   1.904 +{
   1.905 +  TT_UNKNOWN,
   1.906 +
   1.907 +  TT_SUN, TT_MON, TT_TUE, TT_WED, TT_THU, TT_FRI, TT_SAT,
   1.908 +
   1.909 +  TT_JAN, TT_FEB, TT_MAR, TT_APR, TT_MAY, TT_JUN,
   1.910 +  TT_JUL, TT_AUG, TT_SEP, TT_OCT, TT_NOV, TT_DEC,
   1.911 +
   1.912 +  TT_PST, TT_PDT, TT_MST, TT_MDT, TT_CST, TT_CDT, TT_EST, TT_EDT,
   1.913 +  TT_AST, TT_NST, TT_GMT, TT_BST, TT_MET, TT_EET, TT_JST
   1.914 +} TIME_TOKEN;
   1.915 +
   1.916 +/*
   1.917 + * This parses a time/date string into a PRTime
   1.918 + * (microseconds after "1-Jan-1970 00:00:00 GMT").
   1.919 + * It returns PR_SUCCESS on success, and PR_FAILURE
   1.920 + * if the time/date string can't be parsed.
   1.921 + *
   1.922 + * Many formats are handled, including:
   1.923 + *
   1.924 + *   14 Apr 89 03:20:12
   1.925 + *   14 Apr 89 03:20 GMT
   1.926 + *   Fri, 17 Mar 89 4:01:33
   1.927 + *   Fri, 17 Mar 89 4:01 GMT
   1.928 + *   Mon Jan 16 16:12 PDT 1989
   1.929 + *   Mon Jan 16 16:12 +0130 1989
   1.930 + *   6 May 1992 16:41-JST (Wednesday)
   1.931 + *   22-AUG-1993 10:59:12.82
   1.932 + *   22-AUG-1993 10:59pm
   1.933 + *   22-AUG-1993 12:59am
   1.934 + *   22-AUG-1993 12:59 PM
   1.935 + *   Friday, August 04, 1995 3:54 PM
   1.936 + *   06/21/95 04:24:34 PM
   1.937 + *   20/06/95 21:07
   1.938 + *   95-06-08 19:32:48 EDT
   1.939 + *
   1.940 + * If the input string doesn't contain a description of the timezone,
   1.941 + * we consult the `default_to_gmt' to decide whether the string should
   1.942 + * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE).
   1.943 + * The correct value for this argument depends on what standard specified
   1.944 + * the time string which you are parsing.
   1.945 + */
   1.946 +
   1.947 +PR_IMPLEMENT(PRStatus)
   1.948 +PR_ParseTimeStringToExplodedTime(
   1.949 +        const char *string,
   1.950 +        PRBool default_to_gmt,
   1.951 +        PRExplodedTime *result)
   1.952 +{
   1.953 +  TIME_TOKEN dotw = TT_UNKNOWN;
   1.954 +  TIME_TOKEN month = TT_UNKNOWN;
   1.955 +  TIME_TOKEN zone = TT_UNKNOWN;
   1.956 +  int zone_offset = -1;
   1.957 +  int dst_offset = 0;
   1.958 +  int date = -1;
   1.959 +  PRInt32 year = -1;
   1.960 +  int hour = -1;
   1.961 +  int min = -1;
   1.962 +  int sec = -1;
   1.963 +
   1.964 +  const char *rest = string;
   1.965 +
   1.966 +  int iterations = 0;
   1.967 +
   1.968 +  PR_ASSERT(string && result);
   1.969 +  if (!string || !result) return PR_FAILURE;
   1.970 +
   1.971 +  while (*rest)
   1.972 +        {
   1.973 +
   1.974 +          if (iterations++ > 1000)
   1.975 +                {
   1.976 +                  return PR_FAILURE;
   1.977 +                }
   1.978 +
   1.979 +          switch (*rest)
   1.980 +                {
   1.981 +                case 'a': case 'A':
   1.982 +                  if (month == TT_UNKNOWN &&
   1.983 +                          (rest[1] == 'p' || rest[1] == 'P') &&
   1.984 +                          (rest[2] == 'r' || rest[2] == 'R'))
   1.985 +                        month = TT_APR;
   1.986 +                  else if (zone == TT_UNKNOWN &&
   1.987 +                                   (rest[1] == 's' || rest[1] == 'S') &&
   1.988 +                                   (rest[2] == 't' || rest[2] == 'T'))
   1.989 +                        zone = TT_AST;
   1.990 +                  else if (month == TT_UNKNOWN &&
   1.991 +                                   (rest[1] == 'u' || rest[1] == 'U') &&
   1.992 +                                   (rest[2] == 'g' || rest[2] == 'G'))
   1.993 +                        month = TT_AUG;
   1.994 +                  break;
   1.995 +                case 'b': case 'B':
   1.996 +                  if (zone == TT_UNKNOWN &&
   1.997 +                          (rest[1] == 's' || rest[1] == 'S') &&
   1.998 +                          (rest[2] == 't' || rest[2] == 'T'))
   1.999 +                        zone = TT_BST;
  1.1000 +                  break;
  1.1001 +                case 'c': case 'C':
  1.1002 +                  if (zone == TT_UNKNOWN &&
  1.1003 +                          (rest[1] == 'd' || rest[1] == 'D') &&
  1.1004 +                          (rest[2] == 't' || rest[2] == 'T'))
  1.1005 +                        zone = TT_CDT;
  1.1006 +                  else if (zone == TT_UNKNOWN &&
  1.1007 +                                   (rest[1] == 's' || rest[1] == 'S') &&
  1.1008 +                                   (rest[2] == 't' || rest[2] == 'T'))
  1.1009 +                        zone = TT_CST;
  1.1010 +                  break;
  1.1011 +                case 'd': case 'D':
  1.1012 +                  if (month == TT_UNKNOWN &&
  1.1013 +                          (rest[1] == 'e' || rest[1] == 'E') &&
  1.1014 +                          (rest[2] == 'c' || rest[2] == 'C'))
  1.1015 +                        month = TT_DEC;
  1.1016 +                  break;
  1.1017 +                case 'e': case 'E':
  1.1018 +                  if (zone == TT_UNKNOWN &&
  1.1019 +                          (rest[1] == 'd' || rest[1] == 'D') &&
  1.1020 +                          (rest[2] == 't' || rest[2] == 'T'))
  1.1021 +                        zone = TT_EDT;
  1.1022 +                  else if (zone == TT_UNKNOWN &&
  1.1023 +                                   (rest[1] == 'e' || rest[1] == 'E') &&
  1.1024 +                                   (rest[2] == 't' || rest[2] == 'T'))
  1.1025 +                        zone = TT_EET;
  1.1026 +                  else if (zone == TT_UNKNOWN &&
  1.1027 +                                   (rest[1] == 's' || rest[1] == 'S') &&
  1.1028 +                                   (rest[2] == 't' || rest[2] == 'T'))
  1.1029 +                        zone = TT_EST;
  1.1030 +                  break;
  1.1031 +                case 'f': case 'F':
  1.1032 +                  if (month == TT_UNKNOWN &&
  1.1033 +                          (rest[1] == 'e' || rest[1] == 'E') &&
  1.1034 +                          (rest[2] == 'b' || rest[2] == 'B'))
  1.1035 +                        month = TT_FEB;
  1.1036 +                  else if (dotw == TT_UNKNOWN &&
  1.1037 +                                   (rest[1] == 'r' || rest[1] == 'R') &&
  1.1038 +                                   (rest[2] == 'i' || rest[2] == 'I'))
  1.1039 +                        dotw = TT_FRI;
  1.1040 +                  break;
  1.1041 +                case 'g': case 'G':
  1.1042 +                  if (zone == TT_UNKNOWN &&
  1.1043 +                          (rest[1] == 'm' || rest[1] == 'M') &&
  1.1044 +                          (rest[2] == 't' || rest[2] == 'T'))
  1.1045 +                        zone = TT_GMT;
  1.1046 +                  break;
  1.1047 +                case 'j': case 'J':
  1.1048 +                  if (month == TT_UNKNOWN &&
  1.1049 +                          (rest[1] == 'a' || rest[1] == 'A') &&
  1.1050 +                          (rest[2] == 'n' || rest[2] == 'N'))
  1.1051 +                        month = TT_JAN;
  1.1052 +                  else if (zone == TT_UNKNOWN &&
  1.1053 +                                   (rest[1] == 's' || rest[1] == 'S') &&
  1.1054 +                                   (rest[2] == 't' || rest[2] == 'T'))
  1.1055 +                        zone = TT_JST;
  1.1056 +                  else if (month == TT_UNKNOWN &&
  1.1057 +                                   (rest[1] == 'u' || rest[1] == 'U') &&
  1.1058 +                                   (rest[2] == 'l' || rest[2] == 'L'))
  1.1059 +                        month = TT_JUL;
  1.1060 +                  else if (month == TT_UNKNOWN &&
  1.1061 +                                   (rest[1] == 'u' || rest[1] == 'U') &&
  1.1062 +                                   (rest[2] == 'n' || rest[2] == 'N'))
  1.1063 +                        month = TT_JUN;
  1.1064 +                  break;
  1.1065 +                case 'm': case 'M':
  1.1066 +                  if (month == TT_UNKNOWN &&
  1.1067 +                          (rest[1] == 'a' || rest[1] == 'A') &&
  1.1068 +                          (rest[2] == 'r' || rest[2] == 'R'))
  1.1069 +                        month = TT_MAR;
  1.1070 +                  else if (month == TT_UNKNOWN &&
  1.1071 +                                   (rest[1] == 'a' || rest[1] == 'A') &&
  1.1072 +                                   (rest[2] == 'y' || rest[2] == 'Y'))
  1.1073 +                        month = TT_MAY;
  1.1074 +                  else if (zone == TT_UNKNOWN &&
  1.1075 +                                   (rest[1] == 'd' || rest[1] == 'D') &&
  1.1076 +                                   (rest[2] == 't' || rest[2] == 'T'))
  1.1077 +                        zone = TT_MDT;
  1.1078 +                  else if (zone == TT_UNKNOWN &&
  1.1079 +                                   (rest[1] == 'e' || rest[1] == 'E') &&
  1.1080 +                                   (rest[2] == 't' || rest[2] == 'T'))
  1.1081 +                        zone = TT_MET;
  1.1082 +                  else if (dotw == TT_UNKNOWN &&
  1.1083 +                                   (rest[1] == 'o' || rest[1] == 'O') &&
  1.1084 +                                   (rest[2] == 'n' || rest[2] == 'N'))
  1.1085 +                        dotw = TT_MON;
  1.1086 +                  else if (zone == TT_UNKNOWN &&
  1.1087 +                                   (rest[1] == 's' || rest[1] == 'S') &&
  1.1088 +                                   (rest[2] == 't' || rest[2] == 'T'))
  1.1089 +                        zone = TT_MST;
  1.1090 +                  break;
  1.1091 +                case 'n': case 'N':
  1.1092 +                  if (month == TT_UNKNOWN &&
  1.1093 +                          (rest[1] == 'o' || rest[1] == 'O') &&
  1.1094 +                          (rest[2] == 'v' || rest[2] == 'V'))
  1.1095 +                        month = TT_NOV;
  1.1096 +                  else if (zone == TT_UNKNOWN &&
  1.1097 +                                   (rest[1] == 's' || rest[1] == 'S') &&
  1.1098 +                                   (rest[2] == 't' || rest[2] == 'T'))
  1.1099 +                        zone = TT_NST;
  1.1100 +                  break;
  1.1101 +                case 'o': case 'O':
  1.1102 +                  if (month == TT_UNKNOWN &&
  1.1103 +                          (rest[1] == 'c' || rest[1] == 'C') &&
  1.1104 +                          (rest[2] == 't' || rest[2] == 'T'))
  1.1105 +                        month = TT_OCT;
  1.1106 +                  break;
  1.1107 +                case 'p': case 'P':
  1.1108 +                  if (zone == TT_UNKNOWN &&
  1.1109 +                          (rest[1] == 'd' || rest[1] == 'D') &&
  1.1110 +                          (rest[2] == 't' || rest[2] == 'T'))
  1.1111 +                        zone = TT_PDT;
  1.1112 +                  else if (zone == TT_UNKNOWN &&
  1.1113 +                                   (rest[1] == 's' || rest[1] == 'S') &&
  1.1114 +                                   (rest[2] == 't' || rest[2] == 'T'))
  1.1115 +                        zone = TT_PST;
  1.1116 +                  break;
  1.1117 +                case 's': case 'S':
  1.1118 +                  if (dotw == TT_UNKNOWN &&
  1.1119 +                          (rest[1] == 'a' || rest[1] == 'A') &&
  1.1120 +                          (rest[2] == 't' || rest[2] == 'T'))
  1.1121 +                        dotw = TT_SAT;
  1.1122 +                  else if (month == TT_UNKNOWN &&
  1.1123 +                                   (rest[1] == 'e' || rest[1] == 'E') &&
  1.1124 +                                   (rest[2] == 'p' || rest[2] == 'P'))
  1.1125 +                        month = TT_SEP;
  1.1126 +                  else if (dotw == TT_UNKNOWN &&
  1.1127 +                                   (rest[1] == 'u' || rest[1] == 'U') &&
  1.1128 +                                   (rest[2] == 'n' || rest[2] == 'N'))
  1.1129 +                        dotw = TT_SUN;
  1.1130 +                  break;
  1.1131 +                case 't': case 'T':
  1.1132 +                  if (dotw == TT_UNKNOWN &&
  1.1133 +                          (rest[1] == 'h' || rest[1] == 'H') &&
  1.1134 +                          (rest[2] == 'u' || rest[2] == 'U'))
  1.1135 +                        dotw = TT_THU;
  1.1136 +                  else if (dotw == TT_UNKNOWN &&
  1.1137 +                                   (rest[1] == 'u' || rest[1] == 'U') &&
  1.1138 +                                   (rest[2] == 'e' || rest[2] == 'E'))
  1.1139 +                        dotw = TT_TUE;
  1.1140 +                  break;
  1.1141 +                case 'u': case 'U':
  1.1142 +                  if (zone == TT_UNKNOWN &&
  1.1143 +                          (rest[1] == 't' || rest[1] == 'T') &&
  1.1144 +                          !(rest[2] >= 'A' && rest[2] <= 'Z') &&
  1.1145 +                          !(rest[2] >= 'a' && rest[2] <= 'z'))
  1.1146 +                        /* UT is the same as GMT but UTx is not. */
  1.1147 +                        zone = TT_GMT;
  1.1148 +                  break;
  1.1149 +                case 'w': case 'W':
  1.1150 +                  if (dotw == TT_UNKNOWN &&
  1.1151 +                          (rest[1] == 'e' || rest[1] == 'E') &&
  1.1152 +                          (rest[2] == 'd' || rest[2] == 'D'))
  1.1153 +                        dotw = TT_WED;
  1.1154 +                  break;
  1.1155 +
  1.1156 +                case '+': case '-':
  1.1157 +                  {
  1.1158 +                        const char *end;
  1.1159 +                        int sign;
  1.1160 +                        if (zone_offset != -1)
  1.1161 +                          {
  1.1162 +                                /* already got one... */
  1.1163 +                                rest++;
  1.1164 +                                break;
  1.1165 +                          }
  1.1166 +                        if (zone != TT_UNKNOWN && zone != TT_GMT)
  1.1167 +                          {
  1.1168 +                                /* GMT+0300 is legal, but PST+0300 is not. */
  1.1169 +                                rest++;
  1.1170 +                                break;
  1.1171 +                          }
  1.1172 +
  1.1173 +                        sign = ((*rest == '+') ? 1 : -1);
  1.1174 +                        rest++; /* move over sign */
  1.1175 +                        end = rest;
  1.1176 +                        while (*end >= '0' && *end <= '9')
  1.1177 +                          end++;
  1.1178 +                        if (rest == end) /* no digits here */
  1.1179 +                          break;
  1.1180 +
  1.1181 +                        if ((end - rest) == 4)
  1.1182 +                          /* offset in HHMM */
  1.1183 +                          zone_offset = (((((rest[0]-'0')*10) + (rest[1]-'0')) * 60) +
  1.1184 +                                                         (((rest[2]-'0')*10) + (rest[3]-'0')));
  1.1185 +                        else if ((end - rest) == 2)
  1.1186 +                          /* offset in hours */
  1.1187 +                          zone_offset = (((rest[0]-'0')*10) + (rest[1]-'0')) * 60;
  1.1188 +                        else if ((end - rest) == 1)
  1.1189 +                          /* offset in hours */
  1.1190 +                          zone_offset = (rest[0]-'0') * 60;
  1.1191 +                        else
  1.1192 +                          /* 3 or >4 */
  1.1193 +                          break;
  1.1194 +
  1.1195 +                        zone_offset *= sign;
  1.1196 +                        zone = TT_GMT;
  1.1197 +                        break;
  1.1198 +                  }
  1.1199 +
  1.1200 +                case '0': case '1': case '2': case '3': case '4':
  1.1201 +                case '5': case '6': case '7': case '8': case '9':
  1.1202 +                  {
  1.1203 +                        int tmp_hour = -1;
  1.1204 +                        int tmp_min = -1;
  1.1205 +                        int tmp_sec = -1;
  1.1206 +                        const char *end = rest + 1;
  1.1207 +                        while (*end >= '0' && *end <= '9')
  1.1208 +                          end++;
  1.1209 +
  1.1210 +                        /* end is now the first character after a range of digits. */
  1.1211 +
  1.1212 +                        if (*end == ':')
  1.1213 +                          {
  1.1214 +                                if (hour >= 0 && min >= 0) /* already got it */
  1.1215 +                                  break;
  1.1216 +
  1.1217 +                                /* We have seen "[0-9]+:", so this is probably HH:MM[:SS] */
  1.1218 +                                if ((end - rest) > 2)
  1.1219 +                                  /* it is [0-9][0-9][0-9]+: */
  1.1220 +                                  break;
  1.1221 +                                else if ((end - rest) == 2)
  1.1222 +                                  tmp_hour = ((rest[0]-'0')*10 +
  1.1223 +                                                          (rest[1]-'0'));
  1.1224 +                                else
  1.1225 +                                  tmp_hour = (rest[0]-'0');
  1.1226 +
  1.1227 +                                /* move over the colon, and parse minutes */
  1.1228 +
  1.1229 +                                rest = ++end;
  1.1230 +                                while (*end >= '0' && *end <= '9')
  1.1231 +                                  end++;
  1.1232 +
  1.1233 +                                if (end == rest)
  1.1234 +                                  /* no digits after first colon? */
  1.1235 +                                  break;
  1.1236 +                                else if ((end - rest) > 2)
  1.1237 +                                  /* it is [0-9][0-9][0-9]+: */
  1.1238 +                                  break;
  1.1239 +                                else if ((end - rest) == 2)
  1.1240 +                                  tmp_min = ((rest[0]-'0')*10 +
  1.1241 +                                                         (rest[1]-'0'));
  1.1242 +                                else
  1.1243 +                                  tmp_min = (rest[0]-'0');
  1.1244 +
  1.1245 +                                /* now go for seconds */
  1.1246 +                                rest = end;
  1.1247 +                                if (*rest == ':')
  1.1248 +                                  rest++;
  1.1249 +                                end = rest;
  1.1250 +                                while (*end >= '0' && *end <= '9')
  1.1251 +                                  end++;
  1.1252 +
  1.1253 +                                if (end == rest)
  1.1254 +                                  /* no digits after second colon - that's ok. */
  1.1255 +                                  ;
  1.1256 +                                else if ((end - rest) > 2)
  1.1257 +                                  /* it is [0-9][0-9][0-9]+: */
  1.1258 +                                  break;
  1.1259 +                                else if ((end - rest) == 2)
  1.1260 +                                  tmp_sec = ((rest[0]-'0')*10 +
  1.1261 +                                                         (rest[1]-'0'));
  1.1262 +                                else
  1.1263 +                                  tmp_sec = (rest[0]-'0');
  1.1264 +
  1.1265 +                                /* If we made it here, we've parsed hour and min,
  1.1266 +                                   and possibly sec, so it worked as a unit. */
  1.1267 +
  1.1268 +                                /* skip over whitespace and see if there's an AM or PM
  1.1269 +                                   directly following the time.
  1.1270 +                                 */
  1.1271 +                                if (tmp_hour <= 12)
  1.1272 +                                  {
  1.1273 +                                        const char *s = end;
  1.1274 +                                        while (*s && (*s == ' ' || *s == '\t'))
  1.1275 +                                          s++;
  1.1276 +                                        if ((s[0] == 'p' || s[0] == 'P') &&
  1.1277 +                                                (s[1] == 'm' || s[1] == 'M'))
  1.1278 +                                          /* 10:05pm == 22:05, and 12:05pm == 12:05 */
  1.1279 +                                          tmp_hour = (tmp_hour == 12 ? 12 : tmp_hour + 12);
  1.1280 +                                        else if (tmp_hour == 12 &&
  1.1281 +                                                         (s[0] == 'a' || s[0] == 'A') &&
  1.1282 +                                                         (s[1] == 'm' || s[1] == 'M'))
  1.1283 +                                          /* 12:05am == 00:05 */
  1.1284 +                                          tmp_hour = 0;
  1.1285 +                                  }
  1.1286 +
  1.1287 +                                hour = tmp_hour;
  1.1288 +                                min = tmp_min;
  1.1289 +                                sec = tmp_sec;
  1.1290 +                                rest = end;
  1.1291 +                                break;
  1.1292 +                          }
  1.1293 +                        else if ((*end == '/' || *end == '-') &&
  1.1294 +                                         end[1] >= '0' && end[1] <= '9')
  1.1295 +                          {
  1.1296 +                                /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95
  1.1297 +                                   or even 95-06-05...
  1.1298 +                                   #### But it doesn't handle 1995-06-22.
  1.1299 +                                 */
  1.1300 +                                int n1, n2, n3;
  1.1301 +                                const char *s;
  1.1302 +
  1.1303 +                                if (month != TT_UNKNOWN)
  1.1304 +                                  /* if we saw a month name, this can't be. */
  1.1305 +                                  break;
  1.1306 +
  1.1307 +                                s = rest;
  1.1308 +
  1.1309 +                                n1 = (*s++ - '0');                                /* first 1 or 2 digits */
  1.1310 +                                if (*s >= '0' && *s <= '9')
  1.1311 +                                  n1 = n1*10 + (*s++ - '0');
  1.1312 +
  1.1313 +                                if (*s != '/' && *s != '-')                /* slash */
  1.1314 +                                  break;
  1.1315 +                                s++;
  1.1316 +
  1.1317 +                                if (*s < '0' || *s > '9')                /* second 1 or 2 digits */
  1.1318 +                                  break;
  1.1319 +                                n2 = (*s++ - '0');
  1.1320 +                                if (*s >= '0' && *s <= '9')
  1.1321 +                                  n2 = n2*10 + (*s++ - '0');
  1.1322 +
  1.1323 +                                if (*s != '/' && *s != '-')                /* slash */
  1.1324 +                                  break;
  1.1325 +                                s++;
  1.1326 +
  1.1327 +                                if (*s < '0' || *s > '9')                /* third 1, 2, 4, or 5 digits */
  1.1328 +                                  break;
  1.1329 +                                n3 = (*s++ - '0');
  1.1330 +                                if (*s >= '0' && *s <= '9')
  1.1331 +                                  n3 = n3*10 + (*s++ - '0');
  1.1332 +
  1.1333 +                                if (*s >= '0' && *s <= '9')            /* optional digits 3, 4, and 5 */
  1.1334 +                                  {
  1.1335 +                                        n3 = n3*10 + (*s++ - '0');
  1.1336 +                                        if (*s < '0' || *s > '9')
  1.1337 +                                          break;
  1.1338 +                                        n3 = n3*10 + (*s++ - '0');
  1.1339 +                                        if (*s >= '0' && *s <= '9')
  1.1340 +                                          n3 = n3*10 + (*s++ - '0');
  1.1341 +                                  }
  1.1342 +
  1.1343 +                                if ((*s >= '0' && *s <= '9') ||        /* followed by non-alphanum */
  1.1344 +                                        (*s >= 'A' && *s <= 'Z') ||
  1.1345 +                                        (*s >= 'a' && *s <= 'z'))
  1.1346 +                                  break;
  1.1347 +
  1.1348 +                                /* Ok, we parsed three 1-2 digit numbers, with / or -
  1.1349 +                                   between them.  Now decide what the hell they are
  1.1350 +                                   (DD/MM/YY or MM/DD/YY or YY/MM/DD.)
  1.1351 +                                 */
  1.1352 +
  1.1353 +                                if (n1 > 31 || n1 == 0)  /* must be YY/MM/DD */
  1.1354 +                                  {
  1.1355 +                                        if (n2 > 12) break;
  1.1356 +                                        if (n3 > 31) break;
  1.1357 +                                        year = n1;
  1.1358 +                                        if (year < 70)
  1.1359 +                                            year += 2000;
  1.1360 +                                        else if (year < 100)
  1.1361 +                                            year += 1900;
  1.1362 +                                        month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
  1.1363 +                                        date = n3;
  1.1364 +                                        rest = s;
  1.1365 +                                        break;
  1.1366 +                                  }
  1.1367 +
  1.1368 +                                if (n1 > 12 && n2 > 12)  /* illegal */
  1.1369 +                                  {
  1.1370 +                                        rest = s;
  1.1371 +                                        break;
  1.1372 +                                  }
  1.1373 +
  1.1374 +                                if (n3 < 70)
  1.1375 +                                    n3 += 2000;
  1.1376 +                                else if (n3 < 100)
  1.1377 +                                    n3 += 1900;
  1.1378 +
  1.1379 +                                if (n1 > 12)  /* must be DD/MM/YY */
  1.1380 +                                  {
  1.1381 +                                        date = n1;
  1.1382 +                                        month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
  1.1383 +                                        year = n3;
  1.1384 +                                  }
  1.1385 +                                else                  /* assume MM/DD/YY */
  1.1386 +                                  {
  1.1387 +                                        /* #### In the ambiguous case, should we consult the
  1.1388 +                                           locale to find out the local default? */
  1.1389 +                                        month = (TIME_TOKEN)(n1 + ((int)TT_JAN) - 1);
  1.1390 +                                        date = n2;
  1.1391 +                                        year = n3;
  1.1392 +                                  }
  1.1393 +                                rest = s;
  1.1394 +                          }
  1.1395 +                        else if ((*end >= 'A' && *end <= 'Z') ||
  1.1396 +                                         (*end >= 'a' && *end <= 'z'))
  1.1397 +                          /* Digits followed by non-punctuation - what's that? */
  1.1398 +                          ;
  1.1399 +                        else if ((end - rest) == 5)                /* five digits is a year */
  1.1400 +                          year = (year < 0
  1.1401 +                                          ? ((rest[0]-'0')*10000L +
  1.1402 +                                                 (rest[1]-'0')*1000L +
  1.1403 +                                                 (rest[2]-'0')*100L +
  1.1404 +                                                 (rest[3]-'0')*10L +
  1.1405 +                                                 (rest[4]-'0'))
  1.1406 +                                          : year);
  1.1407 +                        else if ((end - rest) == 4)                /* four digits is a year */
  1.1408 +                          year = (year < 0
  1.1409 +                                          ? ((rest[0]-'0')*1000L +
  1.1410 +                                                 (rest[1]-'0')*100L +
  1.1411 +                                                 (rest[2]-'0')*10L +
  1.1412 +                                                 (rest[3]-'0'))
  1.1413 +                                          : year);
  1.1414 +                        else if ((end - rest) == 2)                /* two digits - date or year */
  1.1415 +                          {
  1.1416 +                                int n = ((rest[0]-'0')*10 +
  1.1417 +                                                 (rest[1]-'0'));
  1.1418 +                                /* If we don't have a date (day of the month) and we see a number
  1.1419 +                                     less than 32, then assume that is the date.
  1.1420 +
  1.1421 +                                         Otherwise, if we have a date and not a year, assume this is the
  1.1422 +                                         year.  If it is less than 70, then assume it refers to the 21st
  1.1423 +                                         century.  If it is two digits (>= 70), assume it refers to this
  1.1424 +                                         century.  Otherwise, assume it refers to an unambiguous year.
  1.1425 +
  1.1426 +                                         The world will surely end soon.
  1.1427 +                                   */
  1.1428 +                                if (date < 0 && n < 32)
  1.1429 +                                  date = n;
  1.1430 +                                else if (year < 0)
  1.1431 +                                  {
  1.1432 +                                        if (n < 70)
  1.1433 +                                          year = 2000 + n;
  1.1434 +                                        else if (n < 100)
  1.1435 +                                          year = 1900 + n;
  1.1436 +                                        else
  1.1437 +                                          year = n;
  1.1438 +                                  }
  1.1439 +                                /* else what the hell is this. */
  1.1440 +                          }
  1.1441 +                        else if ((end - rest) == 1)                /* one digit - date */
  1.1442 +                          date = (date < 0 ? (rest[0]-'0') : date);
  1.1443 +                        /* else, three or more than five digits - what's that? */
  1.1444 +
  1.1445 +                        break;
  1.1446 +                  }
  1.1447 +                }
  1.1448 +
  1.1449 +          /* Skip to the end of this token, whether we parsed it or not.
  1.1450 +                 Tokens are delimited by whitespace, or ,;-/
  1.1451 +                 But explicitly not :+-.
  1.1452 +           */
  1.1453 +          while (*rest &&
  1.1454 +                         *rest != ' ' && *rest != '\t' &&
  1.1455 +                         *rest != ',' && *rest != ';' &&
  1.1456 +                         *rest != '-' && *rest != '+' &&
  1.1457 +                         *rest != '/' &&
  1.1458 +                         *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']')
  1.1459 +                rest++;
  1.1460 +          /* skip over uninteresting chars. */
  1.1461 +        SKIP_MORE:
  1.1462 +          while (*rest &&
  1.1463 +                         (*rest == ' ' || *rest == '\t' ||
  1.1464 +                          *rest == ',' || *rest == ';' || *rest == '/' ||
  1.1465 +                          *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']'))
  1.1466 +                rest++;
  1.1467 +
  1.1468 +          /* "-" is ignored at the beginning of a token if we have not yet
  1.1469 +                 parsed a year (e.g., the second "-" in "30-AUG-1966"), or if
  1.1470 +                 the character after the dash is not a digit. */         
  1.1471 +          if (*rest == '-' && ((rest > string &&
  1.1472 +              isalpha((unsigned char)rest[-1]) && year < 0) ||
  1.1473 +              rest[1] < '0' || rest[1] > '9'))
  1.1474 +                {
  1.1475 +                  rest++;
  1.1476 +                  goto SKIP_MORE;
  1.1477 +                }
  1.1478 +
  1.1479 +        }
  1.1480 +
  1.1481 +  if (zone != TT_UNKNOWN && zone_offset == -1)
  1.1482 +        {
  1.1483 +          switch (zone)
  1.1484 +                {
  1.1485 +                case TT_PST: zone_offset = -8 * 60; break;
  1.1486 +                case TT_PDT: zone_offset = -8 * 60; dst_offset = 1 * 60; break;
  1.1487 +                case TT_MST: zone_offset = -7 * 60; break;
  1.1488 +                case TT_MDT: zone_offset = -7 * 60; dst_offset = 1 * 60; break;
  1.1489 +                case TT_CST: zone_offset = -6 * 60; break;
  1.1490 +                case TT_CDT: zone_offset = -6 * 60; dst_offset = 1 * 60; break;
  1.1491 +                case TT_EST: zone_offset = -5 * 60; break;
  1.1492 +                case TT_EDT: zone_offset = -5 * 60; dst_offset = 1 * 60; break;
  1.1493 +                case TT_AST: zone_offset = -4 * 60; break;
  1.1494 +                case TT_NST: zone_offset = -3 * 60 - 30; break;
  1.1495 +                case TT_GMT: zone_offset =  0 * 60; break;
  1.1496 +                case TT_BST: zone_offset =  0 * 60; dst_offset = 1 * 60; break;
  1.1497 +                case TT_MET: zone_offset =  1 * 60; break;
  1.1498 +                case TT_EET: zone_offset =  2 * 60; break;
  1.1499 +                case TT_JST: zone_offset =  9 * 60; break;
  1.1500 +                default:
  1.1501 +                  PR_ASSERT (0);
  1.1502 +                  break;
  1.1503 +                }
  1.1504 +        }
  1.1505 +
  1.1506 +  /* If we didn't find a year, month, or day-of-the-month, we can't
  1.1507 +         possibly parse this, and in fact, mktime() will do something random
  1.1508 +         (I'm seeing it return "Tue Feb  5 06:28:16 2036", which is no doubt
  1.1509 +         a numerologically significant date... */
  1.1510 +  if (month == TT_UNKNOWN || date == -1 || year == -1 || year > PR_INT16_MAX)
  1.1511 +      return PR_FAILURE;
  1.1512 +
  1.1513 +  memset(result, 0, sizeof(*result));
  1.1514 +  if (sec != -1)
  1.1515 +        result->tm_sec = sec;
  1.1516 +  if (min != -1)
  1.1517 +        result->tm_min = min;
  1.1518 +  if (hour != -1)
  1.1519 +        result->tm_hour = hour;
  1.1520 +  if (date != -1)
  1.1521 +        result->tm_mday = date;
  1.1522 +  if (month != TT_UNKNOWN)
  1.1523 +        result->tm_month = (((int)month) - ((int)TT_JAN));
  1.1524 +  if (year != -1)
  1.1525 +        result->tm_year = year;
  1.1526 +  if (dotw != TT_UNKNOWN)
  1.1527 +        result->tm_wday = (((int)dotw) - ((int)TT_SUN));
  1.1528 +  /*
  1.1529 +   * Mainly to compute wday and yday, but normalized time is also required
  1.1530 +   * by the check below that works around a Visual C++ 2005 mktime problem.
  1.1531 +   */
  1.1532 +  PR_NormalizeTime(result, PR_GMTParameters);
  1.1533 +  /* The remaining work is to set the gmt and dst offsets in tm_params. */
  1.1534 +
  1.1535 +  if (zone == TT_UNKNOWN && default_to_gmt)
  1.1536 +        {
  1.1537 +          /* No zone was specified, so pretend the zone was GMT. */
  1.1538 +          zone = TT_GMT;
  1.1539 +          zone_offset = 0;
  1.1540 +        }
  1.1541 +
  1.1542 +  if (zone_offset == -1)
  1.1543 +         {
  1.1544 +           /* no zone was specified, and we're to assume that everything
  1.1545 +             is local. */
  1.1546 +          struct tm localTime;
  1.1547 +          time_t secs;
  1.1548 +
  1.1549 +          PR_ASSERT(result->tm_month > -1 &&
  1.1550 +                    result->tm_mday > 0 &&
  1.1551 +                    result->tm_hour > -1 &&
  1.1552 +                    result->tm_min > -1 &&
  1.1553 +                    result->tm_sec > -1);
  1.1554 +
  1.1555 +            /*
  1.1556 +             * To obtain time_t from a tm structure representing the local
  1.1557 +             * time, we call mktime().  However, we need to see if we are
  1.1558 +             * on 1-Jan-1970 or before.  If we are, we can't call mktime()
  1.1559 +             * because mktime() will crash on win16. In that case, we
  1.1560 +             * calculate zone_offset based on the zone offset at 
  1.1561 +             * 00:00:00, 2 Jan 1970 GMT, and subtract zone_offset from the
  1.1562 +             * date we are parsing to transform the date to GMT.  We also
  1.1563 +             * do so if mktime() returns (time_t) -1 (time out of range).
  1.1564 +           */
  1.1565 +
  1.1566 +          /* month, day, hours, mins and secs are always non-negative
  1.1567 +             so we dont need to worry about them. */  
  1.1568 +          if(result->tm_year >= 1970)
  1.1569 +                {
  1.1570 +                  PRInt64 usec_per_sec;
  1.1571 +
  1.1572 +                  localTime.tm_sec = result->tm_sec;
  1.1573 +                  localTime.tm_min = result->tm_min;
  1.1574 +                  localTime.tm_hour = result->tm_hour;
  1.1575 +                  localTime.tm_mday = result->tm_mday;
  1.1576 +                  localTime.tm_mon = result->tm_month;
  1.1577 +                  localTime.tm_year = result->tm_year - 1900;
  1.1578 +                  /* Set this to -1 to tell mktime "I don't care".  If you set
  1.1579 +                     it to 0 or 1, you are making assertions about whether the
  1.1580 +                     date you are handing it is in daylight savings mode or not;
  1.1581 +                     and if you're wrong, it will "fix" it for you. */
  1.1582 +                  localTime.tm_isdst = -1;
  1.1583 +
  1.1584 +#if _MSC_VER == 1400  /* 1400 = Visual C++ 2005 (8.0) */
  1.1585 +                  /*
  1.1586 +                   * mktime will return (time_t) -1 if the input is a date
  1.1587 +                   * after 23:59:59, December 31, 3000, US Pacific Time (not
  1.1588 +                   * UTC as documented): 
  1.1589 +                   * http://msdn.microsoft.com/en-us/library/d1y53h2a(VS.80).aspx
  1.1590 +                   * But if the year is 3001, mktime also invokes the invalid
  1.1591 +                   * parameter handler, causing the application to crash.  This
  1.1592 +                   * problem has been reported in
  1.1593 +                   * http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=266036.
  1.1594 +                   * We avoid this crash by not calling mktime if the date is
  1.1595 +                   * out of range.  To use a simple test that works in any time
  1.1596 +                   * zone, we consider year 3000 out of range as well.  (See
  1.1597 +                   * bug 480740.)
  1.1598 +                   */
  1.1599 +                  if (result->tm_year >= 3000) {
  1.1600 +                      /* Emulate what mktime would have done. */
  1.1601 +                      errno = EINVAL;
  1.1602 +                      secs = (time_t) -1;
  1.1603 +                  } else {
  1.1604 +                      secs = mktime(&localTime);
  1.1605 +                  }
  1.1606 +#else
  1.1607 +                  secs = mktime(&localTime);
  1.1608 +#endif
  1.1609 +                  if (secs != (time_t) -1)
  1.1610 +                    {
  1.1611 +                      PRTime usecs64;
  1.1612 +                      LL_I2L(usecs64, secs);
  1.1613 +                      LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
  1.1614 +                      LL_MUL(usecs64, usecs64, usec_per_sec);
  1.1615 +                      PR_ExplodeTime(usecs64, PR_LocalTimeParameters, result);
  1.1616 +                      return PR_SUCCESS;
  1.1617 +                    }
  1.1618 +                }
  1.1619 +                
  1.1620 +                /* So mktime() can't handle this case.  We assume the
  1.1621 +                   zone_offset for the date we are parsing is the same as
  1.1622 +                   the zone offset on 00:00:00 2 Jan 1970 GMT. */
  1.1623 +                secs = 86400;
  1.1624 +                (void) MT_safe_localtime(&secs, &localTime);
  1.1625 +                zone_offset = localTime.tm_min
  1.1626 +                              + 60 * localTime.tm_hour
  1.1627 +                              + 1440 * (localTime.tm_mday - 2);
  1.1628 +        }
  1.1629 +
  1.1630 +  result->tm_params.tp_gmt_offset = zone_offset * 60;
  1.1631 +  result->tm_params.tp_dst_offset = dst_offset * 60;
  1.1632 +
  1.1633 +  return PR_SUCCESS;
  1.1634 +}
  1.1635 +
  1.1636 +PR_IMPLEMENT(PRStatus)
  1.1637 +PR_ParseTimeString(
  1.1638 +        const char *string,
  1.1639 +        PRBool default_to_gmt,
  1.1640 +        PRTime *result)
  1.1641 +{
  1.1642 +  PRExplodedTime tm;
  1.1643 +  PRStatus rv;
  1.1644 +
  1.1645 +  rv = PR_ParseTimeStringToExplodedTime(string,
  1.1646 +                                        default_to_gmt,
  1.1647 +                                        &tm);
  1.1648 +  if (rv != PR_SUCCESS)
  1.1649 +        return rv;
  1.1650 +
  1.1651 +  *result = PR_ImplodeTime(&tm);
  1.1652 +
  1.1653 +  return PR_SUCCESS;
  1.1654 +}
  1.1655 +
  1.1656 +/*
  1.1657 + *******************************************************************
  1.1658 + *******************************************************************
  1.1659 + **
  1.1660 + **    OLD COMPATIBILITY FUNCTIONS
  1.1661 + **
  1.1662 + *******************************************************************
  1.1663 + *******************************************************************
  1.1664 + */
  1.1665 +
  1.1666 +
  1.1667 +/*
  1.1668 + *-----------------------------------------------------------------------
  1.1669 + *
  1.1670 + * PR_FormatTime --
  1.1671 + *
  1.1672 + *     Format a time value into a buffer. Same semantics as strftime().
  1.1673 + *
  1.1674 + *-----------------------------------------------------------------------
  1.1675 + */
  1.1676 +
  1.1677 +PR_IMPLEMENT(PRUint32)
  1.1678 +PR_FormatTime(char *buf, int buflen, const char *fmt, const PRExplodedTime *tm)
  1.1679 +{
  1.1680 +    size_t rv;
  1.1681 +    struct tm a;
  1.1682 +    struct tm *ap;
  1.1683 +
  1.1684 +    if (tm) {
  1.1685 +        ap = &a;
  1.1686 +        a.tm_sec = tm->tm_sec;
  1.1687 +        a.tm_min = tm->tm_min;
  1.1688 +        a.tm_hour = tm->tm_hour;
  1.1689 +        a.tm_mday = tm->tm_mday;
  1.1690 +        a.tm_mon = tm->tm_month;
  1.1691 +        a.tm_wday = tm->tm_wday;
  1.1692 +        a.tm_year = tm->tm_year - 1900;
  1.1693 +        a.tm_yday = tm->tm_yday;
  1.1694 +        a.tm_isdst = tm->tm_params.tp_dst_offset ? 1 : 0;
  1.1695 +
  1.1696 +        /*
  1.1697 +         * On some platforms, for example SunOS 4, struct tm has two
  1.1698 +         * additional fields: tm_zone and tm_gmtoff.
  1.1699 +         */
  1.1700 +
  1.1701 +#if (__GLIBC__ >= 2) || defined(XP_BEOS) \
  1.1702 +        || defined(NETBSD) || defined(OPENBSD) || defined(FREEBSD) \
  1.1703 +        || defined(DARWIN) || defined(SYMBIAN) || defined(ANDROID)
  1.1704 +        a.tm_zone = NULL;
  1.1705 +        a.tm_gmtoff = tm->tm_params.tp_gmt_offset +
  1.1706 +                      tm->tm_params.tp_dst_offset;
  1.1707 +#endif
  1.1708 +    } else {
  1.1709 +        ap = NULL;
  1.1710 +    }
  1.1711 +
  1.1712 +    rv = strftime(buf, buflen, fmt, ap);
  1.1713 +    if (!rv && buf && buflen > 0) {
  1.1714 +        /*
  1.1715 +         * When strftime fails, the contents of buf are indeterminate.
  1.1716 +         * Some callers don't check the return value from this function,
  1.1717 +         * so store an empty string in buf in case they try to print it.
  1.1718 +         */
  1.1719 +        buf[0] = '\0';
  1.1720 +    }
  1.1721 +    return rv;
  1.1722 +}
  1.1723 +
  1.1724 +
  1.1725 +/*
  1.1726 + * The following string arrays and macros are used by PR_FormatTimeUSEnglish().
  1.1727 + */
  1.1728 +
  1.1729 +static const char* abbrevDays[] =
  1.1730 +{
  1.1731 +   "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
  1.1732 +};
  1.1733 +
  1.1734 +static const char* days[] =
  1.1735 +{
  1.1736 +   "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"
  1.1737 +};
  1.1738 +
  1.1739 +static const char* abbrevMonths[] =
  1.1740 +{
  1.1741 +   "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1.1742 +   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  1.1743 +};
  1.1744 +
  1.1745 +static const char* months[] =
  1.1746 +{ 
  1.1747 +    "January", "February", "March", "April", "May", "June",
  1.1748 +    "July", "August", "September", "October", "November", "December"
  1.1749 +};
  1.1750 +
  1.1751 +
  1.1752 +/*
  1.1753 + * Add a single character to the given buffer, incrementing the buffer pointer
  1.1754 + * and decrementing the buffer size. Return 0 on error.
  1.1755 + */
  1.1756 +#define ADDCHAR( buf, bufSize, ch )             \
  1.1757 +do                                              \
  1.1758 +{                                               \
  1.1759 +   if( bufSize < 1 )                            \
  1.1760 +   {                                            \
  1.1761 +      *(--buf) = '\0';                          \
  1.1762 +      return 0;                                 \
  1.1763 +   }                                            \
  1.1764 +   *buf++ = ch;                                 \
  1.1765 +   bufSize--;                                   \
  1.1766 +}                                               \
  1.1767 +while(0)
  1.1768 +
  1.1769 +
  1.1770 +/*
  1.1771 + * Add a string to the given buffer, incrementing the buffer pointer
  1.1772 + * and decrementing the buffer size appropriately.  Return 0 on error.
  1.1773 + */
  1.1774 +#define ADDSTR( buf, bufSize, str )             \
  1.1775 +do                                              \
  1.1776 +{                                               \
  1.1777 +   PRUint32 strSize = strlen( str );              \
  1.1778 +   if( strSize > bufSize )                      \
  1.1779 +   {                                            \
  1.1780 +      if( bufSize==0 )                          \
  1.1781 +         *(--buf) = '\0';                       \
  1.1782 +      else                                      \
  1.1783 +         *buf = '\0';                           \
  1.1784 +      return 0;                                 \
  1.1785 +   }                                            \
  1.1786 +   memcpy(buf, str, strSize);                   \
  1.1787 +   buf += strSize;                              \
  1.1788 +   bufSize -= strSize;                          \
  1.1789 +}                                               \
  1.1790 +while(0)
  1.1791 +
  1.1792 +/* Needed by PR_FormatTimeUSEnglish() */
  1.1793 +static unsigned int  pr_WeekOfYear(const PRExplodedTime* time,
  1.1794 +        unsigned int firstDayOfWeek);
  1.1795 +
  1.1796 +
  1.1797 +/***********************************************************************************
  1.1798 + *
  1.1799 + * Description:
  1.1800 + *  This is a dumbed down version of strftime that will format the date in US
  1.1801 + *  English regardless of the setting of the global locale.  This functionality is
  1.1802 + *  needed to write things like MIME headers which must always be in US English.
  1.1803 + *
  1.1804 + **********************************************************************************/
  1.1805 +             
  1.1806 +PR_IMPLEMENT(PRUint32)
  1.1807 +PR_FormatTimeUSEnglish( char* buf, PRUint32 bufSize,
  1.1808 +                        const char* format, const PRExplodedTime* time )
  1.1809 +{
  1.1810 +   char*         bufPtr = buf;
  1.1811 +   const char*   fmtPtr;
  1.1812 +   char          tmpBuf[ 40 ];        
  1.1813 +   const int     tmpBufSize = sizeof( tmpBuf );
  1.1814 +
  1.1815 +   
  1.1816 +   for( fmtPtr=format; *fmtPtr != '\0'; fmtPtr++ )
  1.1817 +   {
  1.1818 +      if( *fmtPtr != '%' )
  1.1819 +      {
  1.1820 +         ADDCHAR( bufPtr, bufSize, *fmtPtr );
  1.1821 +      }
  1.1822 +      else
  1.1823 +      {
  1.1824 +         switch( *(++fmtPtr) )
  1.1825 +         {
  1.1826 +         case '%':
  1.1827 +            /* escaped '%' character */
  1.1828 +            ADDCHAR( bufPtr, bufSize, '%' );
  1.1829 +            break;
  1.1830 +            
  1.1831 +         case 'a':
  1.1832 +            /* abbreviated weekday name */
  1.1833 +            ADDSTR( bufPtr, bufSize, abbrevDays[ time->tm_wday ] );
  1.1834 +            break;
  1.1835 +               
  1.1836 +         case 'A':
  1.1837 +            /* full weekday name */
  1.1838 +            ADDSTR( bufPtr, bufSize, days[ time->tm_wday ] );
  1.1839 +            break;
  1.1840 +        
  1.1841 +         case 'b':
  1.1842 +            /* abbreviated month name */
  1.1843 +            ADDSTR( bufPtr, bufSize, abbrevMonths[ time->tm_month ] );
  1.1844 +            break;
  1.1845 +        
  1.1846 +         case 'B':
  1.1847 +            /* full month name */
  1.1848 +            ADDSTR(bufPtr, bufSize,  months[ time->tm_month ] );
  1.1849 +            break;
  1.1850 +        
  1.1851 +         case 'c':
  1.1852 +            /* Date and time. */
  1.1853 +            PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%a %b %d %H:%M:%S %Y", time );
  1.1854 +            ADDSTR( bufPtr, bufSize, tmpBuf );
  1.1855 +            break;
  1.1856 +        
  1.1857 +         case 'd':
  1.1858 +            /* day of month ( 01 - 31 ) */
  1.1859 +            PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_mday );
  1.1860 +            ADDSTR( bufPtr, bufSize, tmpBuf ); 
  1.1861 +            break;
  1.1862 +
  1.1863 +         case 'H':
  1.1864 +            /* hour ( 00 - 23 ) */
  1.1865 +            PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_hour );
  1.1866 +            ADDSTR( bufPtr, bufSize, tmpBuf ); 
  1.1867 +            break;
  1.1868 +        
  1.1869 +         case 'I':
  1.1870 +            /* hour ( 01 - 12 ) */
  1.1871 +            PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",
  1.1872 +                        (time->tm_hour%12) ? time->tm_hour%12 : (PRInt32) 12 );
  1.1873 +            ADDSTR( bufPtr, bufSize, tmpBuf ); 
  1.1874 +            break;
  1.1875 +        
  1.1876 +         case 'j':
  1.1877 +            /* day number of year ( 001 - 366 ) */
  1.1878 +            PR_snprintf(tmpBuf,tmpBufSize,"%.3d",time->tm_yday + 1);
  1.1879 +            ADDSTR( bufPtr, bufSize, tmpBuf ); 
  1.1880 +            break;
  1.1881 +        
  1.1882 +         case 'm':
  1.1883 +            /* month number ( 01 - 12 ) */
  1.1884 +            PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_month+1);
  1.1885 +            ADDSTR( bufPtr, bufSize, tmpBuf ); 
  1.1886 +            break;
  1.1887 +        
  1.1888 +         case 'M':
  1.1889 +            /* minute ( 00 - 59 ) */
  1.1890 +            PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_min );
  1.1891 +            ADDSTR( bufPtr, bufSize, tmpBuf ); 
  1.1892 +            break;
  1.1893 +       
  1.1894 +         case 'p':
  1.1895 +            /* locale's equivalent of either AM or PM */
  1.1896 +            ADDSTR( bufPtr, bufSize, (time->tm_hour<12)?"AM":"PM" ); 
  1.1897 +            break;
  1.1898 +        
  1.1899 +         case 'S':
  1.1900 +            /* seconds ( 00 - 61 ), allows for leap seconds */
  1.1901 +            PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_sec );
  1.1902 +            ADDSTR( bufPtr, bufSize, tmpBuf ); 
  1.1903 +            break;
  1.1904 +     
  1.1905 +         case 'U':
  1.1906 +            /* week number of year ( 00 - 53  ),  Sunday  is  the first day of week 1 */
  1.1907 +            PR_snprintf(tmpBuf,tmpBufSize,"%.2d", pr_WeekOfYear( time, 0 ) );
  1.1908 +            ADDSTR( bufPtr, bufSize, tmpBuf );
  1.1909 +            break;
  1.1910 +        
  1.1911 +         case 'w':
  1.1912 +            /* weekday number ( 0 - 6 ), Sunday = 0 */
  1.1913 +            PR_snprintf(tmpBuf,tmpBufSize,"%d",time->tm_wday );
  1.1914 +            ADDSTR( bufPtr, bufSize, tmpBuf ); 
  1.1915 +            break;
  1.1916 +        
  1.1917 +         case 'W':
  1.1918 +            /* Week number of year ( 00 - 53  ),  Monday  is  the first day of week 1 */
  1.1919 +            PR_snprintf(tmpBuf,tmpBufSize,"%.2d", pr_WeekOfYear( time, 1 ) );
  1.1920 +            ADDSTR( bufPtr, bufSize, tmpBuf );
  1.1921 +            break;
  1.1922 +        
  1.1923 +         case 'x':
  1.1924 +            /* Date representation */
  1.1925 +            PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%m/%d/%y", time );
  1.1926 +            ADDSTR( bufPtr, bufSize, tmpBuf );
  1.1927 +            break;
  1.1928 +        
  1.1929 +         case 'X':
  1.1930 +            /* Time representation. */
  1.1931 +            PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%H:%M:%S", time );
  1.1932 +            ADDSTR( bufPtr, bufSize, tmpBuf );
  1.1933 +            break;
  1.1934 +        
  1.1935 +         case 'y':
  1.1936 +            /* year within century ( 00 - 99 ) */
  1.1937 +            PR_snprintf(tmpBuf,tmpBufSize,"%.2d",time->tm_year % 100 );
  1.1938 +            ADDSTR( bufPtr, bufSize, tmpBuf ); 
  1.1939 +            break;
  1.1940 +        
  1.1941 +         case 'Y':
  1.1942 +            /* year as ccyy ( for example 1986 ) */
  1.1943 +            PR_snprintf(tmpBuf,tmpBufSize,"%.4d",time->tm_year );
  1.1944 +            ADDSTR( bufPtr, bufSize, tmpBuf ); 
  1.1945 +            break;
  1.1946 +        
  1.1947 +         case 'Z':
  1.1948 +            /* Time zone name or no characters if  no  time  zone exists.
  1.1949 +             * Since time zone name is supposed to be independant of locale, we
  1.1950 +             * defer to PR_FormatTime() for this option.
  1.1951 +             */
  1.1952 +            PR_FormatTime( tmpBuf, tmpBufSize, "%Z", time );
  1.1953 +            ADDSTR( bufPtr, bufSize, tmpBuf ); 
  1.1954 +            break;
  1.1955 +
  1.1956 +         default:
  1.1957 +            /* Unknown format.  Simply copy format into output buffer. */
  1.1958 +            ADDCHAR( bufPtr, bufSize, '%' );
  1.1959 +            ADDCHAR( bufPtr, bufSize, *fmtPtr );
  1.1960 +            break;
  1.1961 +            
  1.1962 +         }
  1.1963 +      }
  1.1964 +   }
  1.1965 +
  1.1966 +   ADDCHAR( bufPtr, bufSize, '\0' );
  1.1967 +   return (PRUint32)(bufPtr - buf - 1);
  1.1968 +}
  1.1969 +
  1.1970 +
  1.1971 +
  1.1972 +/***********************************************************************************
  1.1973 + *
  1.1974 + * Description:
  1.1975 + *  Returns the week number of the year (0-53) for the given time.  firstDayOfWeek
  1.1976 + *  is the day on which the week is considered to start (0=Sun, 1=Mon, ...).
  1.1977 + *  Week 1 starts the first time firstDayOfWeek occurs in the year.  In other words,
  1.1978 + *  a partial week at the start of the year is considered week 0.  
  1.1979 + *
  1.1980 + **********************************************************************************/
  1.1981 +
  1.1982 +static unsigned int
  1.1983 +pr_WeekOfYear(const PRExplodedTime* time, unsigned int firstDayOfWeek)
  1.1984 +{
  1.1985 +   int dayOfWeek;
  1.1986 +   int dayOfYear;
  1.1987 +
  1.1988 +  /* Get the day of the year for the given time then adjust it to represent the
  1.1989 +   * first day of the week containing the given time.
  1.1990 +   */
  1.1991 +  dayOfWeek = time->tm_wday - firstDayOfWeek;
  1.1992 +  if (dayOfWeek < 0)
  1.1993 +    dayOfWeek += 7;
  1.1994 +  
  1.1995 +  dayOfYear = time->tm_yday - dayOfWeek;
  1.1996 +
  1.1997 +
  1.1998 +  if( dayOfYear <= 0 )
  1.1999 +  {
  1.2000 +     /* If dayOfYear is <= 0, it is in the first partial week of the year. */
  1.2001 +     return 0;
  1.2002 +  }
  1.2003 +  else
  1.2004 +  {
  1.2005 +     /* Count the number of full weeks ( dayOfYear / 7 ) then add a week if there
  1.2006 +      * are any days left over ( dayOfYear % 7 ).  Because we are only counting to
  1.2007 +      * the first day of the week containing the given time, rather than to the
  1.2008 +      * actual day representing the given time, any days in week 0 will be "absorbed"
  1.2009 +      * as extra days in the given week.
  1.2010 +      */
  1.2011 +     return (dayOfYear / 7) + ( (dayOfYear % 7) == 0 ? 0 : 1 );
  1.2012 +  }
  1.2013 +}
  1.2014 +

mercurial