tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

prtime.c (60892B)


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