home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / nsprpub / pr / src / misc / prtime.c < prev   
Encoding:
C/C++ Source or Header  |  1998-04-08  |  68.0 KB  |  1,931 lines

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