Back to index

lightning-sunbird  0.9+nobinonly
prtime.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039  * prtime.c --
00040  *
00041  *     NSPR date and time functions
00042  *
00043  */
00044 
00045 #include "prinit.h"
00046 #include "prtime.h"
00047 #include "prlock.h"
00048 #include "prprf.h"
00049 #include "prlog.h"
00050 
00051 #include <string.h>
00052 #include <ctype.h>
00053 
00054 #ifdef XP_MAC
00055 #include <time.h>
00056 #endif
00057 
00058 
00059 
00060 
00061 /*
00062  * Static variables used by functions in this file
00063  */
00064 
00065 /*
00066  * The following array contains the day of year for the last day of
00067  * each month, where index 1 is January, and day 0 is January 1.
00068  */
00069 
00070 static const int lastDayOfMonth[2][13] = {
00071     {-1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364},
00072     {-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}
00073 };
00074 
00075 /*
00076  * The number of days in a month
00077  */
00078 
00079 static const PRInt8 nDays[2][12] = {
00080     {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
00081     {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
00082 };
00083 
00084 /*
00085  * Declarations for internal functions defined later in this file.
00086  */
00087 
00088 static void        ComputeGMT(PRTime time, PRExplodedTime *gmt);
00089 static int        IsLeapYear(PRInt16 year);
00090 static void        ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset);
00091 
00092 /*
00093  *------------------------------------------------------------------------
00094  *
00095  * ComputeGMT --
00096  *
00097  *     Caveats:
00098  *     - we ignore leap seconds
00099  *     - our leap-year calculation is only correct for years 1901-2099
00100  *
00101  *------------------------------------------------------------------------
00102  */
00103 
00104 static void
00105 ComputeGMT(PRTime time, PRExplodedTime *gmt)
00106 {
00107     PRInt32 tmp, rem;
00108     PRInt32 numDays;
00109     PRInt64 numDays64, rem64;
00110     int isLeap;
00111     PRInt64 sec;
00112     PRInt64 usec;
00113     PRInt64 usecPerSec;
00114     PRInt64 secPerDay;
00115 
00116     /*
00117      * We first do the usec, sec, min, hour thing so that we do not
00118      * have to do LL arithmetic.
00119      */
00120 
00121     LL_I2L(usecPerSec, 1000000L);
00122     LL_DIV(sec, time, usecPerSec);
00123     LL_MOD(usec, time, usecPerSec);
00124     LL_L2I(gmt->tm_usec, usec);
00125     /* Correct for weird mod semantics so the remainder is always positive */
00126     if (gmt->tm_usec < 0) {
00127         PRInt64 one;
00128 
00129         LL_I2L(one, 1L);
00130         LL_SUB(sec, sec, one);
00131         gmt->tm_usec += 1000000L;
00132     }
00133 
00134     LL_I2L(secPerDay, 86400L);
00135     LL_DIV(numDays64, sec, secPerDay);
00136     LL_MOD(rem64, sec, secPerDay);
00137     /* We are sure both of these numbers can fit into PRInt32 */
00138     LL_L2I(numDays, numDays64);
00139     LL_L2I(rem, rem64);
00140     if (rem < 0) {
00141         numDays--;
00142         rem += 86400L;
00143     }
00144 
00145     /* Compute day of week.  Epoch started on a Thursday. */
00146 
00147     gmt->tm_wday = (numDays + 4) % 7;
00148     if (gmt->tm_wday < 0) {
00149         gmt->tm_wday += 7;
00150     }
00151 
00152     /* Compute the time of day. */
00153     
00154     gmt->tm_hour = rem / 3600;
00155     rem %= 3600;
00156     gmt->tm_min = rem / 60;
00157     gmt->tm_sec = rem % 60;
00158 
00159     /* Compute the four-year span containing the specified time */
00160 
00161     tmp = numDays / (4 * 365 + 1);
00162     rem = numDays % (4 * 365 + 1);
00163 
00164     if (rem < 0) {
00165         tmp--;
00166         rem += (4 * 365 + 1);
00167     }
00168 
00169     /*
00170      * Compute the year after 1900 by taking the four-year span and
00171      * adjusting for the remainder.  This works because 2000 is a 
00172      * leap year, and 1900 and 2100 are out of the range.
00173      */
00174     
00175     tmp = (tmp * 4) + 1970;
00176     isLeap = 0;
00177 
00178     /*
00179      * 1970 has 365 days
00180      * 1971 has 365 days
00181      * 1972 has 366 days (leap year)
00182      * 1973 has 365 days
00183      */
00184 
00185     if (rem >= 365) {                                /* 1971, etc. */
00186         tmp++;
00187         rem -= 365;
00188         if (rem >= 365) {                        /* 1972, etc. */
00189             tmp++;
00190             rem -= 365;
00191             if (rem >= 366) {                        /* 1973, etc. */
00192                 tmp++;
00193                 rem -= 366;
00194             } else {
00195                 isLeap = 1;
00196             }
00197         }
00198     }
00199 
00200     gmt->tm_year = tmp;
00201     gmt->tm_yday = rem;
00202 
00203     /* Compute the month and day of month. */
00204 
00205     for (tmp = 1; lastDayOfMonth[isLeap][tmp] < gmt->tm_yday; tmp++) {
00206     }
00207     gmt->tm_month = --tmp;
00208     gmt->tm_mday = gmt->tm_yday - lastDayOfMonth[isLeap][tmp];
00209 
00210     gmt->tm_params.tp_gmt_offset = 0;
00211     gmt->tm_params.tp_dst_offset = 0;
00212 }
00213 
00214 
00215 /*
00216  *------------------------------------------------------------------------
00217  *
00218  * PR_ExplodeTime --
00219  *
00220  *     Cf. struct tm *gmtime(const time_t *tp) and
00221  *         struct tm *localtime(const time_t *tp)
00222  *
00223  *------------------------------------------------------------------------
00224  */
00225 
00226 PR_IMPLEMENT(void)
00227 PR_ExplodeTime(
00228         PRTime usecs,
00229         PRTimeParamFn params,
00230         PRExplodedTime *exploded)
00231 {
00232     ComputeGMT(usecs, exploded);
00233     exploded->tm_params = params(exploded);
00234     ApplySecOffset(exploded, exploded->tm_params.tp_gmt_offset
00235             + exploded->tm_params.tp_dst_offset);
00236 }
00237 
00238 
00239 /*
00240  *------------------------------------------------------------------------
00241  *
00242  * PR_ImplodeTime --
00243  *
00244  *     Cf. time_t mktime(struct tm *tp)
00245  *     Note that 1 year has < 2^25 seconds.  So an PRInt32 is large enough.
00246  *
00247  *------------------------------------------------------------------------
00248  */
00249 #if defined(HAVE_WATCOM_BUG_2)
00250 PRTime __pascal __export __loadds
00251 #else
00252 PR_IMPLEMENT(PRTime)
00253 #endif
00254 PR_ImplodeTime(const PRExplodedTime *exploded)
00255 {
00256     PRExplodedTime copy;
00257     PRTime retVal;
00258     PRInt64 secPerDay, usecPerSec;
00259     PRInt64 temp;
00260     PRInt64 numSecs64;
00261     PRInt32 fourYears;
00262     PRInt32 remainder;
00263     PRInt32 numDays;
00264     PRInt32 numSecs;
00265 
00266     /* Normalize first.  Do this on our copy */
00267     copy = *exploded;
00268     PR_NormalizeTime(&copy, PR_GMTParameters);
00269 
00270     fourYears = (copy.tm_year - 1970) / 4;
00271     remainder = (copy.tm_year - 1970) % 4;
00272     if (remainder < 0) {
00273         remainder += 4;
00274         fourYears--;
00275     }
00276     numDays = fourYears * (4 * 365 + 1);
00277     switch (remainder) {
00278         case 0:
00279             break;
00280         case 1:  /* 1970 */
00281             numDays += 365;
00282             break;
00283         case 2:  /* 1970-1 */
00284             numDays += 365 * 2;
00285             break;
00286         case 3:  /* 1970-2 */
00287             numDays += 365 * 3 + 1;
00288             break;
00289     }
00290 
00291     numSecs = copy.tm_yday * 86400 + copy.tm_hour * 3600
00292             + copy.tm_min * 60 + copy.tm_sec;
00293 
00294     LL_I2L(temp, numDays);
00295     LL_I2L(secPerDay, 86400);
00296     LL_MUL(temp, temp, secPerDay);
00297     LL_I2L(numSecs64, numSecs);
00298     LL_ADD(numSecs64, numSecs64, temp);
00299 
00300     /* apply the GMT and DST offsets */
00301     LL_I2L(temp,  copy.tm_params.tp_gmt_offset);
00302     LL_SUB(numSecs64, numSecs64, temp);
00303     LL_I2L(temp,  copy.tm_params.tp_dst_offset);
00304     LL_SUB(numSecs64, numSecs64, temp);
00305 
00306     LL_I2L(usecPerSec, 1000000L);
00307     LL_MUL(temp, numSecs64, usecPerSec);
00308     LL_I2L(retVal, copy.tm_usec);
00309     LL_ADD(retVal, retVal, temp);
00310 
00311     return retVal;
00312 }
00313 
00314 /*
00315  *-------------------------------------------------------------------------
00316  *
00317  * IsLeapYear --
00318  *
00319  *     Returns 1 if the year is a leap year, 0 otherwise.
00320  *
00321  *-------------------------------------------------------------------------
00322  */
00323 
00324 static int IsLeapYear(PRInt16 year)
00325 {
00326     if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
00327         return 1;
00328     else
00329         return 0;
00330 }
00331 
00332 /*
00333  * 'secOffset' should be less than 86400 (i.e., a day).
00334  * 'time' should point to a normalized PRExplodedTime.
00335  */
00336 
00337 static void
00338 ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset)
00339 {
00340     time->tm_sec += secOffset;
00341 
00342     /* Note that in this implementation we do not count leap seconds */
00343     if (time->tm_sec < 0 || time->tm_sec >= 60) {
00344         time->tm_min += time->tm_sec / 60;
00345         time->tm_sec %= 60;
00346         if (time->tm_sec < 0) {
00347             time->tm_sec += 60;
00348             time->tm_min--;
00349         }
00350     }
00351 
00352     if (time->tm_min < 0 || time->tm_min >= 60) {
00353         time->tm_hour += time->tm_min / 60;
00354         time->tm_min %= 60;
00355         if (time->tm_min < 0) {
00356             time->tm_min += 60;
00357             time->tm_hour--;
00358         }
00359     }
00360 
00361     if (time->tm_hour < 0) {
00362         /* Decrement mday, yday, and wday */
00363         time->tm_hour += 24;
00364         time->tm_mday--;
00365         time->tm_yday--;
00366         if (time->tm_mday < 1) {
00367             time->tm_month--;
00368             if (time->tm_month < 0) {
00369                 time->tm_month = 11;
00370                 time->tm_year--;
00371                 if (IsLeapYear(time->tm_year))
00372                     time->tm_yday = 365;
00373                 else
00374                     time->tm_yday = 364;
00375             }
00376             time->tm_mday = nDays[IsLeapYear(time->tm_year)][time->tm_month];
00377         }
00378         time->tm_wday--;
00379         if (time->tm_wday < 0)
00380             time->tm_wday = 6;
00381     } else if (time->tm_hour > 23) {
00382         /* Increment mday, yday, and wday */
00383         time->tm_hour -= 24;
00384         time->tm_mday++;
00385         time->tm_yday++;
00386         if (time->tm_mday >
00387                 nDays[IsLeapYear(time->tm_year)][time->tm_month]) {
00388             time->tm_mday = 1;
00389             time->tm_month++;
00390             if (time->tm_month > 11) {
00391                 time->tm_month = 0;
00392                 time->tm_year++;
00393                 time->tm_yday = 0;
00394             }
00395         }
00396         time->tm_wday++;
00397         if (time->tm_wday > 6)
00398             time->tm_wday = 0;
00399     }
00400 }
00401 
00402 PR_IMPLEMENT(void)
00403 PR_NormalizeTime(PRExplodedTime *time, PRTimeParamFn params)
00404 {
00405     int daysInMonth;
00406     PRInt32 fourYears;
00407     PRInt32 remainder;
00408     PRInt32 numDays;
00409 
00410     /* Get back to GMT */
00411     time->tm_sec -= time->tm_params.tp_gmt_offset
00412             + time->tm_params.tp_dst_offset;
00413     time->tm_params.tp_gmt_offset = 0;
00414     time->tm_params.tp_dst_offset = 0;
00415 
00416     /* Now normalize GMT */
00417 
00418     if (time->tm_usec < 0 || time->tm_usec >= 1000000) {
00419         time->tm_sec +=  time->tm_usec / 1000000;
00420         time->tm_usec %= 1000000;
00421         if (time->tm_usec < 0) {
00422             time->tm_usec += 1000000;
00423             time->tm_sec--;
00424         }
00425     }
00426 
00427     /* Note that we do not count leap seconds in this implementation */
00428     if (time->tm_sec < 0 || time->tm_sec >= 60) {
00429         time->tm_min += time->tm_sec / 60;
00430         time->tm_sec %= 60;
00431         if (time->tm_sec < 0) {
00432             time->tm_sec += 60;
00433             time->tm_min--;
00434         }
00435     }
00436 
00437     if (time->tm_min < 0 || time->tm_min >= 60) {
00438         time->tm_hour += time->tm_min / 60;
00439         time->tm_min %= 60;
00440         if (time->tm_min < 0) {
00441             time->tm_min += 60;
00442             time->tm_hour--;
00443         }
00444     }
00445 
00446     if (time->tm_hour < 0 || time->tm_hour >= 24) {
00447         time->tm_mday += time->tm_hour / 24;
00448         time->tm_hour %= 24;
00449         if (time->tm_hour < 0) {
00450             time->tm_hour += 24;
00451             time->tm_mday--;
00452         }
00453     }
00454 
00455     /* Normalize month and year before mday */
00456     if (time->tm_month < 0 || time->tm_month >= 12) {
00457         time->tm_year += time->tm_month / 12;
00458         time->tm_month %= 12;
00459         if (time->tm_month < 0) {
00460             time->tm_month += 12;
00461             time->tm_year--;
00462         }
00463     }
00464 
00465     /* Now that month and year are in proper range, normalize mday */
00466 
00467     if (time->tm_mday < 1) {
00468         /* mday too small */
00469         do {
00470             /* the previous month */
00471             time->tm_month--;
00472             if (time->tm_month < 0) {
00473                 time->tm_month = 11;
00474                 time->tm_year--;
00475             }
00476             time->tm_mday += nDays[IsLeapYear(time->tm_year)][time->tm_month];
00477         } while (time->tm_mday < 1);
00478     } else {
00479         daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month];
00480         while (time->tm_mday > daysInMonth) {
00481             /* mday too large */
00482             time->tm_mday -= daysInMonth;
00483             time->tm_month++;
00484             if (time->tm_month > 11) {
00485                 time->tm_month = 0;
00486                 time->tm_year++;
00487             }
00488             daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month];
00489         }
00490     }
00491 
00492     /* Recompute yday and wday */
00493     time->tm_yday = time->tm_mday +
00494             lastDayOfMonth[IsLeapYear(time->tm_year)][time->tm_month];
00495     fourYears = (time->tm_year - 1970) / 4;
00496     remainder = (time->tm_year - 1970) % 4;
00497     if (remainder < 0) {
00498         remainder += 4;
00499         fourYears--;
00500     }
00501     numDays = fourYears * (4 * 365 + 1);
00502     switch (remainder) {
00503         case 0:
00504             break;
00505         case 1:
00506             numDays += 365;  /* 1970 */
00507             break;
00508         case 2:
00509             numDays += 365 + 365;  /* 1970 and 1971 */
00510             break;
00511         case 3:
00512             numDays += 365 + 365 + 366; /* 1970-2 */
00513     }
00514     numDays += time->tm_yday;
00515     time->tm_wday = (numDays + 4) % 7;
00516     if (time->tm_wday < 0) {
00517         time->tm_wday += 7;
00518     }
00519 
00520     /* Recompute time parameters */
00521 
00522     time->tm_params = params(time);
00523 
00524     ApplySecOffset(time, time->tm_params.tp_gmt_offset
00525             + time->tm_params.tp_dst_offset);
00526 }
00527 
00528 
00529 /*
00530  *-------------------------------------------------------------------------
00531  *
00532  * PR_LocalTimeParameters --
00533  * 
00534  *     returns the time parameters for the local time zone
00535  *
00536  *     The following uses localtime() from the standard C library.
00537  *     (time.h)  This is our fallback implementation.  Unix and PC
00538  *     use this version.  Mac has its own machine-dependent
00539  *     implementation of this function.
00540  *
00541  *-------------------------------------------------------------------------
00542  */
00543 
00544 #include <time.h>
00545 
00546 #if defined(HAVE_INT_LOCALTIME_R)
00547 
00548 /*
00549  * In this case we could define the macro as
00550  *     #define MT_safe_localtime(timer, result) \
00551  *             (localtime_r(timer, result) == 0 ? result : NULL)
00552  * I chose to compare the return value of localtime_r with -1 so 
00553  * that I can catch the cases where localtime_r returns a pointer
00554  * to struct tm.  The macro definition above would not be able to
00555  * detect such mistakes because it is legal to compare a pointer
00556  * with 0.
00557  */
00558 
00559 #define MT_safe_localtime(timer, result) \
00560         (localtime_r(timer, result) == -1 ? NULL: result)
00561 
00562 #elif defined(HAVE_POINTER_LOCALTIME_R)
00563 
00564 #define MT_safe_localtime localtime_r
00565 
00566 #else
00567 
00568 #if defined(XP_MAC)
00569 extern struct tm *Maclocaltime(const time_t * t);
00570 #endif
00571 
00572 #define HAVE_LOCALTIME_MONITOR 1  /* We use 'monitor' to serialize our calls
00573                                    * to localtime(). */
00574 static PRLock *monitor = NULL;
00575 
00576 static struct tm *MT_safe_localtime(const time_t *clock, struct tm *result)
00577 {
00578     struct tm *tmPtr;
00579     int needLock = PR_Initialized();  /* We need to use a lock to protect
00580                                        * against NSPR threads only when the
00581                                        * NSPR thread system is activated. */
00582 
00583     if (needLock) PR_Lock(monitor);
00584 
00585     /*
00586      * Microsoft (all flavors) localtime() returns a NULL pointer if 'clock'
00587      * represents a time before midnight January 1, 1970.  In
00588      * that case, we also return a NULL pointer and the struct tm
00589      * object pointed to by 'result' is not modified.
00590      *
00591      * Watcom C/C++ 11.0 localtime() treats time_t as unsigned long
00592      * hence, does not recognize negative values of clock as pre-1/1/70.
00593      * We have to manually check (WIN16 only) for negative value of
00594      * clock and return NULL.
00595      *
00596      * With negative values of clock, emx returns the struct tm for
00597      * clock plus ULONG_MAX. So we also have to check for the invalid
00598      * structs returned for timezones west of Greenwich when clock == 0.
00599      */
00600     
00601 #if defined(XP_MAC)
00602     tmPtr = Maclocaltime(clock);
00603 #else
00604     tmPtr = localtime(clock);
00605 #endif
00606 
00607 #if defined(WIN16) || defined(XP_OS2_EMX)
00608     if ( (PRInt32) *clock < 0 ||
00609          ( (PRInt32) *clock == 0 && tmPtr->tm_year != 70))
00610         result = NULL;
00611     else
00612         *result = *tmPtr;
00613 #else
00614     if (tmPtr) {
00615         *result = *tmPtr;
00616     } else {
00617         result = NULL;
00618     }
00619 #endif /* WIN16 */
00620 
00621     if (needLock) PR_Unlock(monitor);
00622 
00623     return result;
00624 }
00625 
00626 #endif  /* definition of MT_safe_localtime() */
00627 
00628 void _PR_InitTime(void)
00629 {
00630 #ifdef HAVE_LOCALTIME_MONITOR
00631     monitor = PR_NewLock();
00632 #endif
00633 }
00634 
00635 void _PR_CleanupTime(void)
00636 {
00637 #ifdef HAVE_LOCALTIME_MONITOR
00638     if (monitor) {
00639         PR_DestroyLock(monitor);
00640         monitor = NULL;
00641     }
00642 #endif
00643 }
00644 
00645 #if defined(XP_UNIX) || defined(XP_PC) || defined(XP_BEOS)
00646 
00647 PR_IMPLEMENT(PRTimeParameters)
00648 PR_LocalTimeParameters(const PRExplodedTime *gmt)
00649 {
00650 
00651     PRTimeParameters retVal;
00652     struct tm localTime;
00653     time_t secs;
00654     PRTime secs64;
00655     PRInt64 usecPerSec;
00656     PRInt64 maxInt32;
00657     PRInt64 minInt32;
00658     PRInt32 dayOffset;
00659     PRInt32 offset2Jan1970;
00660     PRInt32 offsetNew;
00661     int isdst2Jan1970;
00662 
00663     /*
00664      * Calculate the GMT offset.  First, figure out what is
00665      * 00:00:00 Jan. 2, 1970 GMT (which is exactly a day, or 86400
00666      * seconds, since the epoch) in local time.  Then we calculate
00667      * the difference between local time and GMT in seconds:
00668      *     gmt_offset = local_time - GMT
00669      *
00670      * Caveat: the validity of this calculation depends on two
00671      * assumptions:
00672      * 1. Daylight saving time was not in effect on Jan. 2, 1970.
00673      * 2. The time zone of the geographic location has not changed
00674      *    since Jan. 2, 1970.
00675      */
00676 
00677     secs = 86400L;
00678     (void) MT_safe_localtime(&secs, &localTime);
00679 
00680     /* GMT is 00:00:00, 2nd of Jan. */
00681 
00682     offset2Jan1970 = (PRInt32)localTime.tm_sec 
00683             + 60L * (PRInt32)localTime.tm_min
00684             + 3600L * (PRInt32)localTime.tm_hour
00685             + 86400L * (PRInt32)((PRInt32)localTime.tm_mday - 2L);
00686 
00687     isdst2Jan1970 = localTime.tm_isdst;
00688 
00689     /*
00690      * Now compute DST offset.  We calculate the overall offset
00691      * of local time from GMT, similar to above.  The overall
00692      * offset has two components: gmt offset and dst offset.
00693      * We subtract gmt offset from the overall offset to get
00694      * the dst offset.
00695      *     overall_offset = local_time - GMT
00696      *     overall_offset = gmt_offset + dst_offset
00697      * ==> dst_offset = local_time - GMT - gmt_offset
00698      */
00699 
00700     secs64 = PR_ImplodeTime(gmt);    /* This is still in microseconds */
00701     LL_I2L(usecPerSec, PR_USEC_PER_SEC);
00702     LL_DIV(secs64, secs64, usecPerSec);   /* Convert to seconds */
00703     LL_I2L(maxInt32, PR_INT32_MAX);
00704     LL_I2L(minInt32, PR_INT32_MIN);
00705     if (LL_CMP(secs64, >, maxInt32) || LL_CMP(secs64, <, minInt32)) {
00706         /* secs64 is too large or too small for time_t (32-bit integer) */
00707         retVal.tp_gmt_offset = offset2Jan1970;
00708         retVal.tp_dst_offset = 0;
00709         return retVal;
00710     }
00711     LL_L2I(secs, secs64);
00712 
00713     /*
00714      * On Windows, localtime() (and our MT_safe_localtime() too)
00715      * returns a NULL pointer for time before midnight January 1,
00716      * 1970 GMT.  In that case, we just use the GMT offset for
00717      * Jan 2, 1970 and assume that DST was not in effect.
00718      */
00719 
00720     if (MT_safe_localtime(&secs, &localTime) == NULL) {
00721         retVal.tp_gmt_offset = offset2Jan1970;
00722         retVal.tp_dst_offset = 0;
00723         return retVal;
00724     }
00725 
00726     /*
00727      * dayOffset is the offset between local time and GMT in 
00728      * the day component, which can only be -1, 0, or 1.  We
00729      * use the day of the week to compute dayOffset.
00730      */
00731 
00732     dayOffset = (PRInt32) localTime.tm_wday - gmt->tm_wday;
00733 
00734     /*
00735      * Need to adjust for wrapping around of day of the week from
00736      * 6 back to 0.
00737      */
00738 
00739     if (dayOffset == -6) {
00740         /* Local time is Sunday (0) and GMT is Saturday (6) */
00741         dayOffset = 1;
00742     } else if (dayOffset == 6) {
00743         /* Local time is Saturday (6) and GMT is Sunday (0) */
00744         dayOffset = -1;
00745     }
00746 
00747     offsetNew = (PRInt32)localTime.tm_sec - gmt->tm_sec
00748             + 60L * ((PRInt32)localTime.tm_min - gmt->tm_min)
00749             + 3600L * ((PRInt32)localTime.tm_hour - gmt->tm_hour)
00750             + 86400L * (PRInt32)dayOffset;
00751 
00752     if (localTime.tm_isdst <= 0) {
00753         /* DST is not in effect */
00754         retVal.tp_gmt_offset = offsetNew;
00755         retVal.tp_dst_offset = 0;
00756     } else {
00757         /* DST is in effect */
00758         if (isdst2Jan1970 <=0) {
00759             /*
00760              * DST was not in effect back in 2 Jan. 1970.
00761              * Use the offset back then as the GMT offset,
00762              * assuming the time zone has not changed since then.
00763              */
00764             retVal.tp_gmt_offset = offset2Jan1970;
00765             retVal.tp_dst_offset = offsetNew - offset2Jan1970;
00766         } else {
00767             /*
00768              * DST was also in effect back in 2 Jan. 1970.
00769              * Then our clever trick (or rather, ugly hack) fails.
00770              * We will just assume DST offset is an hour.
00771              */
00772             retVal.tp_gmt_offset = offsetNew - 3600;
00773             retVal.tp_dst_offset = 3600;
00774         }
00775     }
00776     
00777     return retVal;
00778 }
00779 
00780 #endif    /* defined(XP_UNIX) !! defined(XP_PC) */
00781 
00782 /*
00783  *------------------------------------------------------------------------
00784  *
00785  * PR_USPacificTimeParameters --
00786  *
00787  *     The time parameters function for the US Pacific Time Zone.
00788  *
00789  *------------------------------------------------------------------------
00790  */
00791 
00792 /*
00793  * Returns the mday of the first sunday of the month, where
00794  * mday and wday are for a given day in the month.
00795  * mdays start with 1 (e.g. 1..31).  
00796  * wdays start with 0 and are in the range 0..6.  0 = Sunday.
00797  */
00798 #define firstSunday(mday, wday) (((mday - wday + 7 - 1) % 7) + 1)
00799 
00800 /*
00801  * Returns the mday for the N'th Sunday of the month, where 
00802  * mday and wday are for a given day in the month.
00803  * mdays start with 1 (e.g. 1..31).  
00804  * wdays start with 0 and are in the range 0..6.  0 = Sunday.
00805  * N has the following values: 0 = first, 1 = second (etc), -1 = last.
00806  * ndays is the number of days in that month, the same value as the 
00807  * mday of the last day of the month.
00808  */
00809 static PRInt32 
00810 NthSunday(PRInt32 mday, PRInt32 wday, PRInt32 N, PRInt32 ndays) 
00811 {
00812     PRInt32 firstSun = firstSunday(mday, wday);
00813 
00814     if (N < 0) 
00815         N = (ndays - firstSun) / 7;
00816     return firstSun + (7 * N);
00817 }
00818 
00819 typedef struct DSTParams {
00820     PRInt8 dst_start_month;       /* 0 = January */
00821     PRInt8 dst_start_Nth_Sunday;  /* N as defined above */
00822     PRInt8 dst_start_month_ndays; /* ndays as defined above */
00823     PRInt8 dst_end_month;         /* 0 = January */
00824     PRInt8 dst_end_Nth_Sunday;    /* N as defined above */
00825     PRInt8 dst_end_month_ndays;   /* ndays as defined above */
00826 } DSTParams;
00827 
00828 static const DSTParams dstParams[2] = {
00829     /* year < 2007:  First April Sunday - Last October Sunday */
00830     { 3, 0, 30, 9, -1, 31 },
00831     /* year >= 2007: Second March Sunday - First November Sunday */
00832     { 2, 1, 31, 10, 0, 30 }
00833 };
00834 
00835 PR_IMPLEMENT(PRTimeParameters)
00836 PR_USPacificTimeParameters(const PRExplodedTime *gmt)
00837 {
00838     const DSTParams *dst;
00839     PRTimeParameters retVal;
00840     PRExplodedTime st;
00841 
00842     /*
00843      * Based on geographic location and GMT, figure out offset of
00844      * standard time from GMT.  In this example implementation, we
00845      * assume the local time zone is US Pacific Time.
00846      */
00847 
00848     retVal.tp_gmt_offset = -8L * 3600L;
00849 
00850     /*
00851      * Make a copy of GMT.  Note that the tm_params field of this copy
00852      * is ignored.
00853      */
00854 
00855     st.tm_usec = gmt->tm_usec;
00856     st.tm_sec = gmt->tm_sec;
00857     st.tm_min = gmt->tm_min;
00858     st.tm_hour = gmt->tm_hour;
00859     st.tm_mday = gmt->tm_mday;
00860     st.tm_month = gmt->tm_month;
00861     st.tm_year = gmt->tm_year;
00862     st.tm_wday = gmt->tm_wday;
00863     st.tm_yday = gmt->tm_yday;
00864 
00865     /* Apply the offset to GMT to obtain the local standard time */
00866     ApplySecOffset(&st, retVal.tp_gmt_offset);
00867 
00868     if (st.tm_year < 2007) { /* first April Sunday - Last October Sunday */
00869        dst = &dstParams[0];
00870     } else {                 /* Second March Sunday - First November Sunday */
00871        dst = &dstParams[1];
00872     }
00873 
00874     /*
00875      * Apply the rules on standard time or GMT to obtain daylight saving
00876      * time offset.  In this implementation, we use the US DST rule.
00877      */
00878     if (st.tm_month < dst->dst_start_month) {
00879         retVal.tp_dst_offset = 0L;
00880     } else if (st.tm_month == dst->dst_start_month) {
00881        int NthSun = NthSunday(st.tm_mday, st.tm_wday, 
00882                             dst->dst_start_Nth_Sunday, 
00883                             dst->dst_start_month_ndays);
00884        if (st.tm_mday < NthSun) {              /* Before starting Sunday */
00885            retVal.tp_dst_offset = 0L;
00886         } else if (st.tm_mday == NthSun) {      /* Starting Sunday */
00887            /* 01:59:59 PST -> 03:00:00 PDT */
00888            if (st.tm_hour < 2) {
00889               retVal.tp_dst_offset = 0L;
00890            } else {
00891               retVal.tp_dst_offset = 3600L;
00892            }
00893        } else {                                /* After starting Sunday */
00894            retVal.tp_dst_offset = 3600L;
00895         }
00896     } else if (st.tm_month < dst->dst_end_month) {
00897         retVal.tp_dst_offset = 3600L;
00898     } else if (st.tm_month == dst->dst_end_month) {
00899        int NthSun = NthSunday(st.tm_mday, st.tm_wday, 
00900                             dst->dst_end_Nth_Sunday, 
00901                             dst->dst_end_month_ndays);
00902        if (st.tm_mday < NthSun) {              /* Before ending Sunday */
00903            retVal.tp_dst_offset = 3600L;
00904         } else if (st.tm_mday == NthSun) {      /* Ending Sunday */
00905            /* 01:59:59 PDT -> 01:00:00 PST */
00906            if (st.tm_hour < 1) {
00907               retVal.tp_dst_offset = 3600L;
00908            } else {
00909               retVal.tp_dst_offset = 0L;
00910            }
00911        } else {                                /* After ending Sunday */
00912            retVal.tp_dst_offset = 0L;
00913         }
00914     } else {
00915         retVal.tp_dst_offset = 0L;
00916     }
00917     return retVal;
00918 }
00919 
00920 /*
00921  *------------------------------------------------------------------------
00922  *
00923  * PR_GMTParameters --
00924  *
00925  *     Returns the PRTimeParameters for Greenwich Mean Time.
00926  *     Trivially, both the tp_gmt_offset and tp_dst_offset fields are 0.
00927  *
00928  *------------------------------------------------------------------------
00929  */
00930 
00931 PR_IMPLEMENT(PRTimeParameters)
00932 PR_GMTParameters(const PRExplodedTime *gmt)
00933 {
00934 #if defined(XP_MAC)
00935 #pragma unused (gmt)
00936 #endif
00937 
00938     PRTimeParameters retVal = { 0, 0 };
00939     return retVal;
00940 }
00941 
00942 /*
00943  * The following code implements PR_ParseTimeString().  It is based on
00944  * ns/lib/xp/xp_time.c, revision 1.25, by Jamie Zawinski <jwz@netscape.com>.
00945  */
00946 
00947 /*
00948  * We only recognize the abbreviations of a small subset of time zones
00949  * in North America, Europe, and Japan.
00950  *
00951  * PST/PDT: Pacific Standard/Daylight Time
00952  * MST/MDT: Mountain Standard/Daylight Time
00953  * CST/CDT: Central Standard/Daylight Time
00954  * EST/EDT: Eastern Standard/Daylight Time
00955  * AST: Atlantic Standard Time
00956  * NST: Newfoundland Standard Time
00957  * GMT: Greenwich Mean Time
00958  * BST: British Summer Time
00959  * MET: Middle Europe Time
00960  * EET: Eastern Europe Time
00961  * JST: Japan Standard Time
00962  */
00963 
00964 typedef enum
00965 {
00966   TT_UNKNOWN,
00967 
00968   TT_SUN, TT_MON, TT_TUE, TT_WED, TT_THU, TT_FRI, TT_SAT,
00969 
00970   TT_JAN, TT_FEB, TT_MAR, TT_APR, TT_MAY, TT_JUN,
00971   TT_JUL, TT_AUG, TT_SEP, TT_OCT, TT_NOV, TT_DEC,
00972 
00973   TT_PST, TT_PDT, TT_MST, TT_MDT, TT_CST, TT_CDT, TT_EST, TT_EDT,
00974   TT_AST, TT_NST, TT_GMT, TT_BST, TT_MET, TT_EET, TT_JST
00975 } TIME_TOKEN;
00976 
00977 /*
00978  * This parses a time/date string into a PRTime
00979  * (microseconds after "1-Jan-1970 00:00:00 GMT").
00980  * It returns PR_SUCCESS on success, and PR_FAILURE
00981  * if the time/date string can't be parsed.
00982  *
00983  * Many formats are handled, including:
00984  *
00985  *   14 Apr 89 03:20:12
00986  *   14 Apr 89 03:20 GMT
00987  *   Fri, 17 Mar 89 4:01:33
00988  *   Fri, 17 Mar 89 4:01 GMT
00989  *   Mon Jan 16 16:12 PDT 1989
00990  *   Mon Jan 16 16:12 +0130 1989
00991  *   6 May 1992 16:41-JST (Wednesday)
00992  *   22-AUG-1993 10:59:12.82
00993  *   22-AUG-1993 10:59pm
00994  *   22-AUG-1993 12:59am
00995  *   22-AUG-1993 12:59 PM
00996  *   Friday, August 04, 1995 3:54 PM
00997  *   06/21/95 04:24:34 PM
00998  *   20/06/95 21:07
00999  *   95-06-08 19:32:48 EDT
01000  *
01001  * If the input string doesn't contain a description of the timezone,
01002  * we consult the `default_to_gmt' to decide whether the string should
01003  * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE).
01004  * The correct value for this argument depends on what standard specified
01005  * the time string which you are parsing.
01006  */
01007 
01008 PR_IMPLEMENT(PRStatus)
01009 PR_ParseTimeString(
01010         const char *string,
01011         PRBool default_to_gmt,
01012         PRTime *result)
01013 {
01014   PRExplodedTime tm;
01015   TIME_TOKEN dotw = TT_UNKNOWN;
01016   TIME_TOKEN month = TT_UNKNOWN;
01017   TIME_TOKEN zone = TT_UNKNOWN;
01018   int zone_offset = -1;
01019   int date = -1;
01020   PRInt32 year = -1;
01021   int hour = -1;
01022   int min = -1;
01023   int sec = -1;
01024 
01025   const char *rest = string;
01026 
01027 #ifdef DEBUG
01028   int iterations = 0;
01029 #endif
01030 
01031   PR_ASSERT(string && result);
01032   if (!string || !result) return PR_FAILURE;
01033 
01034   while (*rest)
01035         {
01036 
01037 #ifdef DEBUG
01038           if (iterations++ > 1000)
01039                 {
01040                   PR_ASSERT(0);
01041                   return PR_FAILURE;
01042                 }
01043 #endif
01044 
01045           switch (*rest)
01046                 {
01047                 case 'a': case 'A':
01048                   if (month == TT_UNKNOWN &&
01049                           (rest[1] == 'p' || rest[1] == 'P') &&
01050                           (rest[2] == 'r' || rest[2] == 'R'))
01051                         month = TT_APR;
01052                   else if (zone == TT_UNKNOWN &&
01053                                    (rest[1] == 's' || rest[1] == 's') &&
01054                                    (rest[2] == 't' || rest[2] == 'T'))
01055                         zone = TT_AST;
01056                   else if (month == TT_UNKNOWN &&
01057                                    (rest[1] == 'u' || rest[1] == 'U') &&
01058                                    (rest[2] == 'g' || rest[2] == 'G'))
01059                         month = TT_AUG;
01060                   break;
01061                 case 'b': case 'B':
01062                   if (zone == TT_UNKNOWN &&
01063                           (rest[1] == 's' || rest[1] == 'S') &&
01064                           (rest[2] == 't' || rest[2] == 'T'))
01065                         zone = TT_BST;
01066                   break;
01067                 case 'c': case 'C':
01068                   if (zone == TT_UNKNOWN &&
01069                           (rest[1] == 'd' || rest[1] == 'D') &&
01070                           (rest[2] == 't' || rest[2] == 'T'))
01071                         zone = TT_CDT;
01072                   else if (zone == TT_UNKNOWN &&
01073                                    (rest[1] == 's' || rest[1] == 'S') &&
01074                                    (rest[2] == 't' || rest[2] == 'T'))
01075                         zone = TT_CST;
01076                   break;
01077                 case 'd': case 'D':
01078                   if (month == TT_UNKNOWN &&
01079                           (rest[1] == 'e' || rest[1] == 'E') &&
01080                           (rest[2] == 'c' || rest[2] == 'C'))
01081                         month = TT_DEC;
01082                   break;
01083                 case 'e': case 'E':
01084                   if (zone == TT_UNKNOWN &&
01085                           (rest[1] == 'd' || rest[1] == 'D') &&
01086                           (rest[2] == 't' || rest[2] == 'T'))
01087                         zone = TT_EDT;
01088                   else if (zone == TT_UNKNOWN &&
01089                                    (rest[1] == 'e' || rest[1] == 'E') &&
01090                                    (rest[2] == 't' || rest[2] == 'T'))
01091                         zone = TT_EET;
01092                   else if (zone == TT_UNKNOWN &&
01093                                    (rest[1] == 's' || rest[1] == 'S') &&
01094                                    (rest[2] == 't' || rest[2] == 'T'))
01095                         zone = TT_EST;
01096                   break;
01097                 case 'f': case 'F':
01098                   if (month == TT_UNKNOWN &&
01099                           (rest[1] == 'e' || rest[1] == 'E') &&
01100                           (rest[2] == 'b' || rest[2] == 'B'))
01101                         month = TT_FEB;
01102                   else if (dotw == TT_UNKNOWN &&
01103                                    (rest[1] == 'r' || rest[1] == 'R') &&
01104                                    (rest[2] == 'i' || rest[2] == 'I'))
01105                         dotw = TT_FRI;
01106                   break;
01107                 case 'g': case 'G':
01108                   if (zone == TT_UNKNOWN &&
01109                           (rest[1] == 'm' || rest[1] == 'M') &&
01110                           (rest[2] == 't' || rest[2] == 'T'))
01111                         zone = TT_GMT;
01112                   break;
01113                 case 'j': case 'J':
01114                   if (month == TT_UNKNOWN &&
01115                           (rest[1] == 'a' || rest[1] == 'A') &&
01116                           (rest[2] == 'n' || rest[2] == 'N'))
01117                         month = TT_JAN;
01118                   else if (zone == TT_UNKNOWN &&
01119                                    (rest[1] == 's' || rest[1] == 'S') &&
01120                                    (rest[2] == 't' || rest[2] == 'T'))
01121                         zone = TT_JST;
01122                   else if (month == TT_UNKNOWN &&
01123                                    (rest[1] == 'u' || rest[1] == 'U') &&
01124                                    (rest[2] == 'l' || rest[2] == 'L'))
01125                         month = TT_JUL;
01126                   else if (month == TT_UNKNOWN &&
01127                                    (rest[1] == 'u' || rest[1] == 'U') &&
01128                                    (rest[2] == 'n' || rest[2] == 'N'))
01129                         month = TT_JUN;
01130                   break;
01131                 case 'm': case 'M':
01132                   if (month == TT_UNKNOWN &&
01133                           (rest[1] == 'a' || rest[1] == 'A') &&
01134                           (rest[2] == 'r' || rest[2] == 'R'))
01135                         month = TT_MAR;
01136                   else if (month == TT_UNKNOWN &&
01137                                    (rest[1] == 'a' || rest[1] == 'A') &&
01138                                    (rest[2] == 'y' || rest[2] == 'Y'))
01139                         month = TT_MAY;
01140                   else if (zone == TT_UNKNOWN &&
01141                                    (rest[1] == 'd' || rest[1] == 'D') &&
01142                                    (rest[2] == 't' || rest[2] == 'T'))
01143                         zone = TT_MDT;
01144                   else if (zone == TT_UNKNOWN &&
01145                                    (rest[1] == 'e' || rest[1] == 'E') &&
01146                                    (rest[2] == 't' || rest[2] == 'T'))
01147                         zone = TT_MET;
01148                   else if (dotw == TT_UNKNOWN &&
01149                                    (rest[1] == 'o' || rest[1] == 'O') &&
01150                                    (rest[2] == 'n' || rest[2] == 'N'))
01151                         dotw = TT_MON;
01152                   else if (zone == TT_UNKNOWN &&
01153                                    (rest[1] == 's' || rest[1] == 'S') &&
01154                                    (rest[2] == 't' || rest[2] == 'T'))
01155                         zone = TT_MST;
01156                   break;
01157                 case 'n': case 'N':
01158                   if (month == TT_UNKNOWN &&
01159                           (rest[1] == 'o' || rest[1] == 'O') &&
01160                           (rest[2] == 'v' || rest[2] == 'V'))
01161                         month = TT_NOV;
01162                   else if (zone == TT_UNKNOWN &&
01163                                    (rest[1] == 's' || rest[1] == 'S') &&
01164                                    (rest[2] == 't' || rest[2] == 'T'))
01165                         zone = TT_NST;
01166                   break;
01167                 case 'o': case 'O':
01168                   if (month == TT_UNKNOWN &&
01169                           (rest[1] == 'c' || rest[1] == 'C') &&
01170                           (rest[2] == 't' || rest[2] == 'T'))
01171                         month = TT_OCT;
01172                   break;
01173                 case 'p': case 'P':
01174                   if (zone == TT_UNKNOWN &&
01175                           (rest[1] == 'd' || rest[1] == 'D') &&
01176                           (rest[2] == 't' || rest[2] == 'T'))
01177                         zone = TT_PDT;
01178                   else if (zone == TT_UNKNOWN &&
01179                                    (rest[1] == 's' || rest[1] == 'S') &&
01180                                    (rest[2] == 't' || rest[2] == 'T'))
01181                         zone = TT_PST;
01182                   break;
01183                 case 's': case 'S':
01184                   if (dotw == TT_UNKNOWN &&
01185                           (rest[1] == 'a' || rest[1] == 'A') &&
01186                           (rest[2] == 't' || rest[2] == 'T'))
01187                         dotw = TT_SAT;
01188                   else if (month == TT_UNKNOWN &&
01189                                    (rest[1] == 'e' || rest[1] == 'E') &&
01190                                    (rest[2] == 'p' || rest[2] == 'P'))
01191                         month = TT_SEP;
01192                   else if (dotw == TT_UNKNOWN &&
01193                                    (rest[1] == 'u' || rest[1] == 'U') &&
01194                                    (rest[2] == 'n' || rest[2] == 'N'))
01195                         dotw = TT_SUN;
01196                   break;
01197                 case 't': case 'T':
01198                   if (dotw == TT_UNKNOWN &&
01199                           (rest[1] == 'h' || rest[1] == 'H') &&
01200                           (rest[2] == 'u' || rest[2] == 'U'))
01201                         dotw = TT_THU;
01202                   else if (dotw == TT_UNKNOWN &&
01203                                    (rest[1] == 'u' || rest[1] == 'U') &&
01204                                    (rest[2] == 'e' || rest[2] == 'E'))
01205                         dotw = TT_TUE;
01206                   break;
01207                 case 'u': case 'U':
01208                   if (zone == TT_UNKNOWN &&
01209                           (rest[1] == 't' || rest[1] == 'T') &&
01210                           !(rest[2] >= 'A' && rest[2] <= 'Z') &&
01211                           !(rest[2] >= 'a' && rest[2] <= 'z'))
01212                         /* UT is the same as GMT but UTx is not. */
01213                         zone = TT_GMT;
01214                   break;
01215                 case 'w': case 'W':
01216                   if (dotw == TT_UNKNOWN &&
01217                           (rest[1] == 'e' || rest[1] == 'E') &&
01218                           (rest[2] == 'd' || rest[2] == 'D'))
01219                         dotw = TT_WED;
01220                   break;
01221 
01222                 case '+': case '-':
01223                   {
01224                         const char *end;
01225                         int sign;
01226                         if (zone_offset != -1)
01227                           {
01228                                 /* already got one... */
01229                                 rest++;
01230                                 break;
01231                           }
01232                         if (zone != TT_UNKNOWN && zone != TT_GMT)
01233                           {
01234                                 /* GMT+0300 is legal, but PST+0300 is not. */
01235                                 rest++;
01236                                 break;
01237                           }
01238 
01239                         sign = ((*rest == '+') ? 1 : -1);
01240                         rest++; /* move over sign */
01241                         end = rest;
01242                         while (*end >= '0' && *end <= '9')
01243                           end++;
01244                         if (rest == end) /* no digits here */
01245                           break;
01246 
01247                         if ((end - rest) == 4)
01248                           /* offset in HHMM */
01249                           zone_offset = (((((rest[0]-'0')*10) + (rest[1]-'0')) * 60) +
01250                                                          (((rest[2]-'0')*10) + (rest[3]-'0')));
01251                         else if ((end - rest) == 2)
01252                           /* offset in hours */
01253                           zone_offset = (((rest[0]-'0')*10) + (rest[1]-'0')) * 60;
01254                         else if ((end - rest) == 1)
01255                           /* offset in hours */
01256                           zone_offset = (rest[0]-'0') * 60;
01257                         else
01258                           /* 3 or >4 */
01259                           break;
01260 
01261                         zone_offset *= sign;
01262                         zone = TT_GMT;
01263                         break;
01264                   }
01265 
01266                 case '0': case '1': case '2': case '3': case '4':
01267                 case '5': case '6': case '7': case '8': case '9':
01268                   {
01269                         int tmp_hour = -1;
01270                         int tmp_min = -1;
01271                         int tmp_sec = -1;
01272                         const char *end = rest + 1;
01273                         while (*end >= '0' && *end <= '9')
01274                           end++;
01275 
01276                         /* end is now the first character after a range of digits. */
01277 
01278                         if (*end == ':')
01279                           {
01280                                 if (hour >= 0 && min >= 0) /* already got it */
01281                                   break;
01282 
01283                                 /* We have seen "[0-9]+:", so this is probably HH:MM[:SS] */
01284                                 if ((end - rest) > 2)
01285                                   /* it is [0-9][0-9][0-9]+: */
01286                                   break;
01287                                 else if ((end - rest) == 2)
01288                                   tmp_hour = ((rest[0]-'0')*10 +
01289                                                           (rest[1]-'0'));
01290                                 else
01291                                   tmp_hour = (rest[0]-'0');
01292 
01293                                 /* move over the colon, and parse minutes */
01294 
01295                                 rest = ++end;
01296                                 while (*end >= '0' && *end <= '9')
01297                                   end++;
01298 
01299                                 if (end == rest)
01300                                   /* no digits after first colon? */
01301                                   break;
01302                                 else if ((end - rest) > 2)
01303                                   /* it is [0-9][0-9][0-9]+: */
01304                                   break;
01305                                 else if ((end - rest) == 2)
01306                                   tmp_min = ((rest[0]-'0')*10 +
01307                                                          (rest[1]-'0'));
01308                                 else
01309                                   tmp_min = (rest[0]-'0');
01310 
01311                                 /* now go for seconds */
01312                                 rest = end;
01313                                 if (*rest == ':')
01314                                   rest++;
01315                                 end = rest;
01316                                 while (*end >= '0' && *end <= '9')
01317                                   end++;
01318 
01319                                 if (end == rest)
01320                                   /* no digits after second colon - that's ok. */
01321                                   ;
01322                                 else if ((end - rest) > 2)
01323                                   /* it is [0-9][0-9][0-9]+: */
01324                                   break;
01325                                 else if ((end - rest) == 2)
01326                                   tmp_sec = ((rest[0]-'0')*10 +
01327                                                          (rest[1]-'0'));
01328                                 else
01329                                   tmp_sec = (rest[0]-'0');
01330 
01331                                 /* If we made it here, we've parsed hour and min,
01332                                    and possibly sec, so it worked as a unit. */
01333 
01334                                 /* skip over whitespace and see if there's an AM or PM
01335                                    directly following the time.
01336                                  */
01337                                 if (tmp_hour <= 12)
01338                                   {
01339                                         const char *s = end;
01340                                         while (*s && (*s == ' ' || *s == '\t'))
01341                                           s++;
01342                                         if ((s[0] == 'p' || s[0] == 'P') &&
01343                                                 (s[1] == 'm' || s[1] == 'M'))
01344                                           /* 10:05pm == 22:05, and 12:05pm == 12:05 */
01345                                           tmp_hour = (tmp_hour == 12 ? 12 : tmp_hour + 12);
01346                                         else if (tmp_hour == 12 &&
01347                                                          (s[0] == 'a' || s[0] == 'A') &&
01348                                                          (s[1] == 'm' || s[1] == 'M'))
01349                                           /* 12:05am == 00:05 */
01350                                           tmp_hour = 0;
01351                                   }
01352 
01353                                 hour = tmp_hour;
01354                                 min = tmp_min;
01355                                 sec = tmp_sec;
01356                                 rest = end;
01357                                 break;
01358                           }
01359                         else if ((*end == '/' || *end == '-') &&
01360                                          end[1] >= '0' && end[1] <= '9')
01361                           {
01362                                 /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95
01363                                    or even 95-06-05...
01364                                    #### But it doesn't handle 1995-06-22.
01365                                  */
01366                                 int n1, n2, n3;
01367                                 const char *s;
01368 
01369                                 if (month != TT_UNKNOWN)
01370                                   /* if we saw a month name, this can't be. */
01371                                   break;
01372 
01373                                 s = rest;
01374 
01375                                 n1 = (*s++ - '0');                                /* first 1 or 2 digits */
01376                                 if (*s >= '0' && *s <= '9')
01377                                   n1 = n1*10 + (*s++ - '0');
01378 
01379                                 if (*s != '/' && *s != '-')                /* slash */
01380                                   break;
01381                                 s++;
01382 
01383                                 if (*s < '0' || *s > '9')                /* second 1 or 2 digits */
01384                                   break;
01385                                 n2 = (*s++ - '0');
01386                                 if (*s >= '0' && *s <= '9')
01387                                   n2 = n2*10 + (*s++ - '0');
01388 
01389                                 if (*s != '/' && *s != '-')                /* slash */
01390                                   break;
01391                                 s++;
01392 
01393                                 if (*s < '0' || *s > '9')                /* third 1, 2, or 4 digits */
01394                                   break;
01395                                 n3 = (*s++ - '0');
01396                                 if (*s >= '0' && *s <= '9')
01397                                   n3 = n3*10 + (*s++ - '0');
01398 
01399                                 if (*s >= '0' && *s <= '9')                /* optional digits 3 and 4 */
01400                                   {
01401                                         n3 = n3*10 + (*s++ - '0');
01402                                         if (*s < '0' || *s > '9')
01403                                           break;
01404                                         n3 = n3*10 + (*s++ - '0');
01405                                   }
01406 
01407                                 if ((*s >= '0' && *s <= '9') ||        /* followed by non-alphanum */
01408                                         (*s >= 'A' && *s <= 'Z') ||
01409                                         (*s >= 'a' && *s <= 'z'))
01410                                   break;
01411 
01412                                 /* Ok, we parsed three 1-2 digit numbers, with / or -
01413                                    between them.  Now decide what the hell they are
01414                                    (DD/MM/YY or MM/DD/YY or YY/MM/DD.)
01415                                  */
01416 
01417                                 if (n1 > 31 || n1 == 0)  /* must be YY/MM/DD */
01418                                   {
01419                                         if (n2 > 12) break;
01420                                         if (n3 > 31) break;
01421                                         year = n1;
01422                                         if (year < 70)
01423                                             year += 2000;
01424                                         else if (year < 100)
01425                                             year += 1900;
01426                                         month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
01427                                         date = n3;
01428                                         rest = s;
01429                                         break;
01430                                   }
01431 
01432                                 if (n1 > 12 && n2 > 12)  /* illegal */
01433                                   {
01434                                         rest = s;
01435                                         break;
01436                                   }
01437 
01438                                 if (n3 < 70)
01439                                     n3 += 2000;
01440                                 else if (n3 < 100)
01441                                     n3 += 1900;
01442 
01443                                 if (n1 > 12)  /* must be DD/MM/YY */
01444                                   {
01445                                         date = n1;
01446                                         month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
01447                                         year = n3;
01448                                   }
01449                                 else                  /* assume MM/DD/YY */
01450                                   {
01451                                         /* #### In the ambiguous case, should we consult the
01452                                            locale to find out the local default? */
01453                                         month = (TIME_TOKEN)(n1 + ((int)TT_JAN) - 1);
01454                                         date = n2;
01455                                         year = n3;
01456                                   }
01457                                 rest = s;
01458                           }
01459                         else if ((*end >= 'A' && *end <= 'Z') ||
01460                                          (*end >= 'a' && *end <= 'z'))
01461                           /* Digits followed by non-punctuation - what's that? */
01462                           ;
01463                         else if ((end - rest) == 4)                /* four digits is a year */
01464                           year = (year < 0
01465                                           ? ((rest[0]-'0')*1000L +
01466                                                  (rest[1]-'0')*100L +
01467                                                  (rest[2]-'0')*10L +
01468                                                  (rest[3]-'0'))
01469                                           : year);
01470                         else if ((end - rest) == 2)                /* two digits - date or year */
01471                           {
01472                                 int n = ((rest[0]-'0')*10 +
01473                                                  (rest[1]-'0'));
01474                                 /* If we don't have a date (day of the month) and we see a number
01475                                      less than 32, then assume that is the date.
01476 
01477                                          Otherwise, if we have a date and not a year, assume this is the
01478                                          year.  If it is less than 70, then assume it refers to the 21st
01479                                          century.  If it is two digits (>= 70), assume it refers to this
01480                                          century.  Otherwise, assume it refers to an unambiguous year.
01481 
01482                                          The world will surely end soon.
01483                                    */
01484                                 if (date < 0 && n < 32)
01485                                   date = n;
01486                                 else if (year < 0)
01487                                   {
01488                                         if (n < 70)
01489                                           year = 2000 + n;
01490                                         else if (n < 100)
01491                                           year = 1900 + n;
01492                                         else
01493                                           year = n;
01494                                   }
01495                                 /* else what the hell is this. */
01496                           }
01497                         else if ((end - rest) == 1)                /* one digit - date */
01498                           date = (date < 0 ? (rest[0]-'0') : date);
01499                         /* else, three or more than four digits - what's that? */
01500 
01501                         break;
01502                   }
01503                 }
01504 
01505           /* Skip to the end of this token, whether we parsed it or not.
01506                  Tokens are delimited by whitespace, or ,;-/
01507                  But explicitly not :+-.
01508            */
01509           while (*rest &&
01510                          *rest != ' ' && *rest != '\t' &&
01511                          *rest != ',' && *rest != ';' &&
01512                          *rest != '-' && *rest != '+' &&
01513                          *rest != '/' &&
01514                          *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']')
01515                 rest++;
01516           /* skip over uninteresting chars. */
01517         SKIP_MORE:
01518           while (*rest &&
01519                          (*rest == ' ' || *rest == '\t' ||
01520                           *rest == ',' || *rest == ';' || *rest == '/' ||
01521                           *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']'))
01522                 rest++;
01523 
01524           /* "-" is ignored at the beginning of a token if we have not yet
01525                  parsed a year (e.g., the second "-" in "30-AUG-1966"), or if
01526                  the character after the dash is not a digit. */         
01527           if (*rest == '-' && ((rest > string && isalpha(rest[-1]) && year < 0)
01528               || rest[1] < '0' || rest[1] > '9'))
01529                 {
01530                   rest++;
01531                   goto SKIP_MORE;
01532                 }
01533 
01534         }
01535 
01536   if (zone != TT_UNKNOWN && zone_offset == -1)
01537         {
01538           switch (zone)
01539                 {
01540                 case TT_PST: zone_offset = -8 * 60; break;
01541                 case TT_PDT: zone_offset = -7 * 60; break;
01542                 case TT_MST: zone_offset = -7 * 60; break;
01543                 case TT_MDT: zone_offset = -6 * 60; break;
01544                 case TT_CST: zone_offset = -6 * 60; break;
01545                 case TT_CDT: zone_offset = -5 * 60; break;
01546                 case TT_EST: zone_offset = -5 * 60; break;
01547                 case TT_EDT: zone_offset = -4 * 60; break;
01548                 case TT_AST: zone_offset = -4 * 60; break;
01549                 case TT_NST: zone_offset = -3 * 60 - 30; break;
01550                 case TT_GMT: zone_offset =  0 * 60; break;
01551                 case TT_BST: zone_offset =  1 * 60; break;
01552                 case TT_MET: zone_offset =  1 * 60; break;
01553                 case TT_EET: zone_offset =  2 * 60; break;
01554                 case TT_JST: zone_offset =  9 * 60; break;
01555                 default:
01556                   PR_ASSERT (0);
01557                   break;
01558                 }
01559         }
01560 
01561   /* If we didn't find a year, month, or day-of-the-month, we can't
01562          possibly parse this, and in fact, mktime() will do something random
01563          (I'm seeing it return "Tue Feb  5 06:28:16 2036", which is no doubt
01564          a numerologically significant date... */
01565   if (month == TT_UNKNOWN || date == -1 || year == -1)
01566       return PR_FAILURE;
01567 
01568   memset(&tm, 0, sizeof(tm));
01569   if (sec != -1)
01570         tm.tm_sec = sec;
01571   if (min != -1)
01572   tm.tm_min = min;
01573   if (hour != -1)
01574         tm.tm_hour = hour;
01575   if (date != -1)
01576         tm.tm_mday = date;
01577   if (month != TT_UNKNOWN)
01578         tm.tm_month = (((int)month) - ((int)TT_JAN));
01579   if (year != -1)
01580         tm.tm_year = year;
01581   if (dotw != TT_UNKNOWN)
01582         tm.tm_wday = (((int)dotw) - ((int)TT_SUN));
01583 
01584   if (zone == TT_UNKNOWN && default_to_gmt)
01585         {
01586           /* No zone was specified, so pretend the zone was GMT. */
01587           zone = TT_GMT;
01588           zone_offset = 0;
01589         }
01590 
01591   if (zone_offset == -1)
01592          {
01593            /* no zone was specified, and we're to assume that everything
01594              is local. */
01595           struct tm localTime;
01596           time_t secs;
01597 
01598           PR_ASSERT(tm.tm_month > -1 
01599                                    && tm.tm_mday > 0 
01600                                    && tm.tm_hour > -1
01601                                    && tm.tm_min > -1
01602                                    && tm.tm_sec > -1);
01603 
01604             /*
01605              * To obtain time_t from a tm structure representing the local
01606              * time, we call mktime().  However, we need to see if we are
01607              * on 1-Jan-1970 or before.  If we are, we can't call mktime()
01608              * because mktime() will crash on win16. In that case, we
01609              * calculate zone_offset based on the zone offset at 
01610              * 00:00:00, 2 Jan 1970 GMT, and subtract zone_offset from the
01611              * date we are parsing to transform the date to GMT.  We also
01612              * do so if mktime() returns (time_t) -1 (time out of range).
01613            */
01614 
01615           /* month, day, hours, mins and secs are always non-negative
01616              so we dont need to worry about them. */  
01617             if(tm.tm_year >= 1970)
01618                 {
01619                   PRInt64 usec_per_sec;
01620 
01621                   localTime.tm_sec = tm.tm_sec;
01622                   localTime.tm_min = tm.tm_min;
01623                   localTime.tm_hour = tm.tm_hour;
01624                   localTime.tm_mday = tm.tm_mday;
01625                   localTime.tm_mon = tm.tm_month;
01626                   localTime.tm_year = tm.tm_year - 1900;
01627                   /* Set this to -1 to tell mktime "I don't care".  If you set
01628                      it to 0 or 1, you are making assertions about whether the
01629                      date you are handing it is in daylight savings mode or not;
01630                      and if you're wrong, it will "fix" it for you. */
01631                   localTime.tm_isdst = -1;
01632                   secs = mktime(&localTime);
01633                   if (secs != (time_t) -1)
01634                     {
01635 #if defined(XP_MAC) && (__MSL__ < 0x6000)
01636                       /*
01637                        * The mktime() routine in MetroWerks MSL C
01638                        * Runtime library returns seconds since midnight,
01639                        * 1 Jan. 1900, not 1970 - in versions of MSL (Metrowerks Standard
01640                        * Library) prior to version 6.  Only for older versions of
01641                        * MSL do we adjust the value of secs to the NSPR epoch
01642                        */
01643                       secs -= ((365 * 70UL) + 17) * 24 * 60 * 60;
01644 #endif
01645                       LL_I2L(*result, secs);
01646                       LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
01647                       LL_MUL(*result, *result, usec_per_sec);
01648                       return PR_SUCCESS;
01649                     }
01650                 }
01651                 
01652                 /* So mktime() can't handle this case.  We assume the
01653                    zone_offset for the date we are parsing is the same as
01654                    the zone offset on 00:00:00 2 Jan 1970 GMT. */
01655                 secs = 86400;
01656                 (void) MT_safe_localtime(&secs, &localTime);
01657                 zone_offset = localTime.tm_min
01658                               + 60 * localTime.tm_hour
01659                               + 1440 * (localTime.tm_mday - 2);
01660         }
01661 
01662         /* Adjust the hours and minutes before handing them to
01663            PR_ImplodeTime(). Note that it's ok for them to be <0 or >24/60 
01664 
01665            We adjust the time to GMT before going into PR_ImplodeTime().
01666            The zone_offset represents the difference between the time
01667            zone parsed and GMT
01668          */
01669         tm.tm_hour -= (zone_offset / 60);
01670         tm.tm_min  -= (zone_offset % 60);
01671 
01672   *result = PR_ImplodeTime(&tm);
01673 
01674   return PR_SUCCESS;
01675 }
01676 
01677 /*
01678  *******************************************************************
01679  *******************************************************************
01680  **
01681  **    OLD COMPATIBILITY FUNCTIONS
01682  **
01683  *******************************************************************
01684  *******************************************************************
01685  */
01686 
01687 
01688 /*
01689  *-----------------------------------------------------------------------
01690  *
01691  * PR_FormatTime --
01692  *
01693  *     Format a time value into a buffer. Same semantics as strftime().
01694  *
01695  *-----------------------------------------------------------------------
01696  */
01697 
01698 PR_IMPLEMENT(PRUint32)
01699 PR_FormatTime(char *buf, int buflen, const char *fmt, const PRExplodedTime *tm)
01700 {
01701     struct tm a;
01702     a.tm_sec = tm->tm_sec;
01703     a.tm_min = tm->tm_min;
01704     a.tm_hour = tm->tm_hour;
01705     a.tm_mday = tm->tm_mday;
01706     a.tm_mon = tm->tm_month;
01707     a.tm_wday = tm->tm_wday;
01708     a.tm_year = tm->tm_year - 1900;
01709     a.tm_yday = tm->tm_yday;
01710     a.tm_isdst = tm->tm_params.tp_dst_offset ? 1 : 0;
01711 
01712 /*
01713  * On some platforms, for example SunOS 4, struct tm has two additional
01714  * fields: tm_zone and tm_gmtoff.
01715  */
01716 
01717 #if defined(SUNOS4) || (__GLIBC__ >= 2) || defined(XP_BEOS) \
01718         || defined(NETBSD) || defined(OPENBSD) || defined(FREEBSD) \
01719         || defined(DARWIN)
01720     a.tm_zone = NULL;
01721     a.tm_gmtoff = tm->tm_params.tp_gmt_offset + tm->tm_params.tp_dst_offset;
01722 #endif
01723 
01724     return strftime(buf, buflen, fmt, &a);
01725 }
01726 
01727 
01728 /*
01729  * The following string arrays and macros are used by PR_FormatTimeUSEnglish().
01730  */
01731 
01732 static const char* abbrevDays[] =
01733 {
01734    "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
01735 };
01736 
01737 static const char* days[] =
01738 {
01739    "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"
01740 };
01741 
01742 static const char* abbrevMonths[] =
01743 {
01744    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
01745    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
01746 };
01747 
01748 static const char* months[] =
01749 { 
01750     "January", "February", "March", "April", "May", "June",
01751     "July", "August", "September", "October", "November", "December"
01752 };
01753 
01754 
01755 /*
01756  * Add a single character to the given buffer, incrementing the buffer pointer
01757  * and decrementing the buffer size. Return 0 on error.
01758  */
01759 #define ADDCHAR( buf, bufSize, ch )             \
01760 do                                              \
01761 {                                               \
01762    if( bufSize < 1 )                            \
01763    {                                            \
01764       *(--buf) = '\0';                          \
01765       return 0;                                 \
01766    }                                            \
01767    *buf++ = ch;                                 \
01768    bufSize--;                                   \
01769 }                                               \
01770 while(0)
01771 
01772 
01773 /*
01774  * Add a string to the given buffer, incrementing the buffer pointer
01775  * and decrementing the buffer size appropriately.  Return 0 on error.
01776  */
01777 #define ADDSTR( buf, bufSize, str )             \
01778 do                                              \
01779 {                                               \
01780    PRUint32 strSize = strlen( str );              \
01781    if( strSize > bufSize )                      \
01782    {                                            \
01783       if( bufSize==0 )                          \
01784          *(--buf) = '\0';                       \
01785       else                                      \
01786          *buf = '\0';                           \
01787       return 0;                                 \
01788    }                                            \
01789    memcpy(buf, str, strSize);                   \
01790    buf += strSize;                              \
01791    bufSize -= strSize;                          \
01792 }                                               \
01793 while(0)
01794 
01795 /* Needed by PR_FormatTimeUSEnglish() */
01796 static unsigned int  pr_WeekOfYear(const PRExplodedTime* time,
01797         unsigned int firstDayOfWeek);
01798 
01799 
01800 /***********************************************************************************
01801  *
01802  * Description:
01803  *  This is a dumbed down version of strftime that will format the date in US
01804  *  English regardless of the setting of the global locale.  This functionality is
01805  *  needed to write things like MIME headers which must always be in US English.
01806  *
01807  **********************************************************************************/
01808              
01809 PR_IMPLEMENT(PRUint32)
01810 PR_FormatTimeUSEnglish( char* buf, PRUint32 bufSize,
01811                         const char* format, const PRExplodedTime* time )
01812 {
01813    char*         bufPtr = buf;
01814    const char*   fmtPtr;
01815    char          tmpBuf[ 40 ];        
01816    const int     tmpBufSize = sizeof( tmpBuf );
01817 
01818    
01819    for( fmtPtr=format; *fmtPtr != '\0'; fmtPtr++ )
01820    {
01821       if( *fmtPtr != '%' )
01822       {
01823          ADDCHAR( bufPtr, bufSize, *fmtPtr );
01824       }
01825       else
01826       {
01827          switch( *(++fmtPtr) )
01828          {
01829          case '%':
01830             /* escaped '%' character */
01831             ADDCHAR( bufPtr, bufSize, '%' );
01832             break;
01833             
01834          case 'a':
01835             /* abbreviated weekday name */
01836             ADDSTR( bufPtr, bufSize, abbrevDays[ time->tm_wday ] );
01837             break;
01838                
01839          case 'A':
01840             /* full weekday name */
01841             ADDSTR( bufPtr, bufSize, days[ time->tm_wday ] );
01842             break;
01843         
01844          case 'b':
01845             /* abbreviated month name */
01846             ADDSTR( bufPtr, bufSize, abbrevMonths[ time->tm_month ] );
01847             break;
01848         
01849          case 'B':
01850             /* full month name */
01851             ADDSTR(bufPtr, bufSize,  months[ time->tm_month ] );
01852             break;
01853         
01854          case 'c':
01855             /* Date and time. */
01856             PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%a %b %d %H:%M:%S %Y", time );
01857             ADDSTR( bufPtr, bufSize, tmpBuf );
01858             break;
01859         
01860          case 'd':
01861             /* day of month ( 01 - 31 ) */
01862             PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_mday );
01863             ADDSTR( bufPtr, bufSize, tmpBuf ); 
01864             break;
01865 
01866          case 'H':
01867             /* hour ( 00 - 23 ) */
01868             PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_hour );
01869             ADDSTR( bufPtr, bufSize, tmpBuf ); 
01870             break;
01871         
01872          case 'I':
01873             /* hour ( 01 - 12 ) */
01874             PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",
01875                         (time->tm_hour%12) ? time->tm_hour%12 : (PRInt32) 12 );
01876             ADDSTR( bufPtr, bufSize, tmpBuf ); 
01877             break;
01878         
01879          case 'j':
01880             /* day number of year ( 001 - 366 ) */
01881             PR_snprintf(tmpBuf,tmpBufSize,"%.3d",time->tm_yday + 1);
01882             ADDSTR( bufPtr, bufSize, tmpBuf ); 
01883             break;
01884         
01885          case 'm':
01886             /* month number ( 01 - 12 ) */
01887             PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_month+1);
01888             ADDSTR( bufPtr, bufSize, tmpBuf ); 
01889             break;
01890         
01891          case 'M':
01892             /* minute ( 00 - 59 ) */
01893             PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_min );
01894             ADDSTR( bufPtr, bufSize, tmpBuf ); 
01895             break;
01896        
01897          case 'p':
01898             /* locale's equivalent of either AM or PM */
01899             ADDSTR( bufPtr, bufSize, (time->tm_hour<12)?"AM":"PM" ); 
01900             break;
01901         
01902          case 'S':
01903             /* seconds ( 00 - 61 ), allows for leap seconds */
01904             PR_snprintf(tmpBuf,tmpBufSize,"%.2ld",time->tm_sec );
01905             ADDSTR( bufPtr, bufSize, tmpBuf ); 
01906             break;
01907      
01908          case 'U':
01909             /* week number of year ( 00 - 53  ),  Sunday  is  the first day of week 1 */
01910             PR_snprintf(tmpBuf,tmpBufSize,"%.2d", pr_WeekOfYear( time, 0 ) );
01911             ADDSTR( bufPtr, bufSize, tmpBuf );
01912             break;
01913         
01914          case 'w':
01915             /* weekday number ( 0 - 6 ), Sunday = 0 */
01916             PR_snprintf(tmpBuf,tmpBufSize,"%d",time->tm_wday );
01917             ADDSTR( bufPtr, bufSize, tmpBuf ); 
01918             break;
01919         
01920          case 'W':
01921             /* Week number of year ( 00 - 53  ),  Monday  is  the first day of week 1 */
01922             PR_snprintf(tmpBuf,tmpBufSize,"%.2d", pr_WeekOfYear( time, 1 ) );
01923             ADDSTR( bufPtr, bufSize, tmpBuf );
01924             break;
01925         
01926          case 'x':
01927             /* Date representation */
01928             PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%m/%d/%y", time );
01929             ADDSTR( bufPtr, bufSize, tmpBuf );
01930             break;
01931         
01932          case 'X':
01933             /* Time representation. */
01934             PR_FormatTimeUSEnglish( tmpBuf, tmpBufSize, "%H:%M:%S", time );
01935             ADDSTR( bufPtr, bufSize, tmpBuf );
01936             break;
01937         
01938          case 'y':
01939             /* year within century ( 00 - 99 ) */
01940             PR_snprintf(tmpBuf,tmpBufSize,"%.2d",time->tm_year % 100 );
01941             ADDSTR( bufPtr, bufSize, tmpBuf ); 
01942             break;
01943         
01944          case 'Y':
01945             /* year as ccyy ( for example 1986 ) */
01946             PR_snprintf(tmpBuf,tmpBufSize,"%.4d",time->tm_year );
01947             ADDSTR( bufPtr, bufSize, tmpBuf ); 
01948             break;
01949         
01950          case 'Z':
01951             /* Time zone name or no characters if  no  time  zone exists.
01952              * Since time zone name is supposed to be independant of locale, we
01953              * defer to PR_FormatTime() for this option.
01954              */
01955             PR_FormatTime( tmpBuf, tmpBufSize, "%Z", time );
01956             ADDSTR( bufPtr, bufSize, tmpBuf ); 
01957             break;
01958 
01959          default:
01960             /* Unknown format.  Simply copy format into output buffer. */
01961             ADDCHAR( bufPtr, bufSize, '%' );
01962             ADDCHAR( bufPtr, bufSize, *fmtPtr );
01963             break;
01964             
01965          }
01966       }
01967    }
01968 
01969    ADDCHAR( bufPtr, bufSize, '\0' );
01970    return (PRUint32)(bufPtr - buf - 1);
01971 }
01972 
01973 
01974 
01975 /***********************************************************************************
01976  *
01977  * Description:
01978  *  Returns the week number of the year (0-53) for the given time.  firstDayOfWeek
01979  *  is the day on which the week is considered to start (0=Sun, 1=Mon, ...).
01980  *  Week 1 starts the first time firstDayOfWeek occurs in the year.  In other words,
01981  *  a partial week at the start of the year is considered week 0.  
01982  *
01983  **********************************************************************************/
01984 
01985 static unsigned int
01986 pr_WeekOfYear(const PRExplodedTime* time, unsigned int firstDayOfWeek)
01987 {
01988    int dayOfWeek;
01989    int dayOfYear;
01990 
01991   /* Get the day of the year for the given time then adjust it to represent the
01992    * first day of the week containing the given time.
01993    */
01994   dayOfWeek = time->tm_wday - firstDayOfWeek;
01995   if (dayOfWeek < 0)
01996     dayOfWeek += 7;
01997   
01998   dayOfYear = time->tm_yday - dayOfWeek;
01999 
02000 
02001   if( dayOfYear <= 0 )
02002   {
02003      /* If dayOfYear is <= 0, it is in the first partial week of the year. */
02004      return 0;
02005   }
02006   else
02007   {
02008      /* Count the number of full weeks ( dayOfYear / 7 ) then add a week if there
02009       * are any days left over ( dayOfYear % 7 ).  Because we are only counting to
02010       * the first day of the week containing the given time, rather than to the
02011       * actual day representing the given time, any days in week 0 will be "absorbed"
02012       * as extra days in the given week.
02013       */
02014      return (dayOfYear / 7) + ( (dayOfYear % 7) == 0 ? 0 : 1 );
02015   }
02016 }
02017