Back to index

lightning-sunbird  0.9+nobinonly
prmjtime.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code, released
00017  * March 31, 1998.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1998
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 /*
00041  * PR time code.
00042  */
00043 #include "jsstddef.h"
00044 #ifdef SOLARIS
00045 #define _REENTRANT 1
00046 #endif
00047 #include <string.h>
00048 #include <time.h>
00049 #include "jstypes.h"
00050 #include "jsutil.h"
00051 
00052 #include "jsprf.h"
00053 #include "prmjtime.h"
00054 
00055 #define PRMJ_DO_MILLISECONDS 1
00056 
00057 #ifdef XP_OS2
00058 #include <sys/timeb.h>
00059 #endif
00060 #ifdef XP_WIN
00061 #include <windef.h>
00062 #include <winbase.h>
00063 #endif
00064 
00065 #if defined(XP_UNIX) || defined(XP_BEOS)
00066 
00067 #ifdef _SVID_GETTOD   /* Defined only on Solaris, see Solaris <sys/types.h> */
00068 extern int gettimeofday(struct timeval *tv);
00069 #endif
00070 
00071 #include <sys/time.h>
00072 
00073 #endif /* XP_UNIX */
00074 
00075 #define IS_LEAP(year) \
00076    (year != 0 && ((((year & 0x3) == 0) &&  \
00077                  ((year - ((year/100) * 100)) != 0)) || \
00078                 (year - ((year/400) * 400)) == 0))
00079 
00080 #define PRMJ_HOUR_SECONDS  3600L
00081 #define PRMJ_DAY_SECONDS  (24L * PRMJ_HOUR_SECONDS)
00082 #define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * 365L)
00083 #define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */
00084 /* function prototypes */
00085 static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm);
00086 /*
00087  * get the difference in seconds between this time zone and UTC (GMT)
00088  */
00089 JSInt32
00090 PRMJ_LocalGMTDifference()
00091 {
00092 #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
00093     struct tm ltime;
00094 
00095     /* get the difference between this time zone and GMT */
00096     memset((char *)&ltime,0,sizeof(ltime));
00097     ltime.tm_mday = 2;
00098     ltime.tm_year = 70;
00099 #ifdef SUNOS4
00100     ltime.tm_zone = 0;
00101     ltime.tm_gmtoff = 0;
00102     return timelocal(&ltime) - (24 * 3600);
00103 #else
00104     return mktime(&ltime) - (24L * 3600L);
00105 #endif
00106 #endif
00107 }
00108 
00109 /* Constants for GMT offset from 1970 */
00110 #define G1970GMTMICROHI        0x00dcdcad /* micro secs to 1970 hi */
00111 #define G1970GMTMICROLOW       0x8b3fa000 /* micro secs to 1970 low */
00112 
00113 #define G2037GMTMICROHI        0x00e45fab /* micro secs to 2037 high */
00114 #define G2037GMTMICROLOW       0x7a238000 /* micro secs to 2037 low */
00115 
00116 /* Convert from base time to extended time */
00117 static JSInt64
00118 PRMJ_ToExtendedTime(JSInt32 base_time)
00119 {
00120     JSInt64 exttime;
00121     JSInt64 g1970GMTMicroSeconds;
00122     JSInt64 low;
00123     JSInt32 diff;
00124     JSInt64  tmp;
00125     JSInt64  tmp1;
00126 
00127     diff = PRMJ_LocalGMTDifference();
00128     JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC);
00129     JSLL_I2L(tmp1,diff);
00130     JSLL_MUL(tmp,tmp,tmp1);
00131 
00132     JSLL_UI2L(g1970GMTMicroSeconds,G1970GMTMICROHI);
00133     JSLL_UI2L(low,G1970GMTMICROLOW);
00134 #ifndef JS_HAVE_LONG_LONG
00135     JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16);
00136     JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16);
00137 #else
00138     JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,32);
00139 #endif
00140     JSLL_ADD(g1970GMTMicroSeconds,g1970GMTMicroSeconds,low);
00141 
00142     JSLL_I2L(exttime,base_time);
00143     JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds);
00144     JSLL_SUB(exttime,exttime,tmp);
00145     return exttime;
00146 }
00147 
00148 JSInt64
00149 PRMJ_Now(void)
00150 {
00151 #ifdef XP_OS2
00152     JSInt64 s, us, ms2us, s2us;
00153     struct timeb b;
00154 #endif
00155 #ifdef XP_WIN
00156     JSInt64 s, us,
00157     win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000),
00158     ten = JSLL_INIT(0, 10);
00159     FILETIME time, midnight;
00160 #endif
00161 #if defined(XP_UNIX) || defined(XP_BEOS)
00162     struct timeval tv;
00163     JSInt64 s, us, s2us;
00164 #endif /* XP_UNIX */
00165 
00166 #ifdef XP_OS2
00167     ftime(&b);
00168     JSLL_UI2L(ms2us, PRMJ_USEC_PER_MSEC);
00169     JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC);
00170     JSLL_UI2L(s, b.time);
00171     JSLL_UI2L(us, b.millitm);
00172     JSLL_MUL(us, us, ms2us);
00173     JSLL_MUL(s, s, s2us);
00174     JSLL_ADD(s, s, us);
00175     return s;
00176 #endif
00177 #ifdef XP_WIN
00178     /* The windows epoch is around 1600. The unix epoch is around 1970.
00179        win2un is the difference (in windows time units which are 10 times
00180        more precise than the JS time unit) */
00181     GetSystemTimeAsFileTime(&time);
00182     /* Win9x gets confused at midnight
00183        http://support.microsoft.com/default.aspx?scid=KB;en-us;q224423
00184        So if the low part (precision <8mins) is 0 then we get the time
00185        again. */
00186     if (!time.dwLowDateTime) {
00187         GetSystemTimeAsFileTime(&midnight);
00188         time.dwHighDateTime = midnight.dwHighDateTime;
00189     }
00190     JSLL_UI2L(s, time.dwHighDateTime);
00191     JSLL_UI2L(us, time.dwLowDateTime);
00192     JSLL_SHL(s, s, 32);
00193     JSLL_ADD(s, s, us);
00194     JSLL_SUB(s, s, win2un);
00195     JSLL_DIV(s, s, ten);
00196     return s;
00197 #endif
00198 
00199 #if defined(XP_UNIX) || defined(XP_BEOS)
00200 #ifdef _SVID_GETTOD   /* Defined only on Solaris, see Solaris <sys/types.h> */
00201     gettimeofday(&tv);
00202 #else
00203     gettimeofday(&tv, 0);
00204 #endif /* _SVID_GETTOD */
00205     JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC);
00206     JSLL_UI2L(s, tv.tv_sec);
00207     JSLL_UI2L(us, tv.tv_usec);
00208     JSLL_MUL(s, s, s2us);
00209     JSLL_ADD(s, s, us);
00210     return s;
00211 #endif /* XP_UNIX */
00212 }
00213 
00214 /* Get the DST timezone offset for the time passed in */
00215 JSInt64
00216 PRMJ_DSTOffset(JSInt64 local_time)
00217 {
00218     JSInt64 us2s;
00219     time_t local;
00220     JSInt32 diff;
00221     JSInt64  maxtimet;
00222     struct tm tm;
00223     PRMJTime prtm;
00224 #ifndef HAVE_LOCALTIME_R
00225     struct tm *ptm;
00226 #endif
00227 
00228 
00229     JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC);
00230     JSLL_DIV(local_time, local_time, us2s);
00231 
00232     /* get the maximum of time_t value */
00233     JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET);
00234 
00235     if(JSLL_CMP(local_time,>,maxtimet)){
00236         JSLL_UI2L(local_time,PRMJ_MAX_UNIX_TIMET);
00237     } else if(!JSLL_GE_ZERO(local_time)){
00238         /*go ahead a day to make localtime work (does not work with 0) */
00239         JSLL_UI2L(local_time,PRMJ_DAY_SECONDS);
00240     }
00241     JSLL_L2UI(local,local_time);
00242     PRMJ_basetime(local_time,&prtm);
00243 #ifndef HAVE_LOCALTIME_R
00244     ptm = localtime(&local);
00245     if(!ptm){
00246         return JSLL_ZERO;
00247     }
00248     tm = *ptm;
00249 #else
00250     localtime_r(&local,&tm); /* get dst information */
00251 #endif
00252 
00253     diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) +
00254        ((tm.tm_min - prtm.tm_min) * 60);
00255 
00256     if(diff < 0){
00257        diff += PRMJ_DAY_SECONDS;
00258     }
00259 
00260     JSLL_UI2L(local_time,diff);
00261 
00262     JSLL_MUL(local_time,local_time,us2s);
00263 
00264     return(local_time);
00265 }
00266 
00267 /* Format a time value into a buffer. Same semantics as strftime() */
00268 size_t
00269 PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *prtm)
00270 {
00271 #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
00272     struct tm a;
00273 
00274     /* Zero out the tm struct.  Linux, SunOS 4 struct tm has extra members int
00275      * tm_gmtoff, char *tm_zone; when tm_zone is garbage, strftime gets
00276      * confused and dumps core.  NSPR20 prtime.c attempts to fill these in by
00277      * calling mktime on the partially filled struct, but this doesn't seem to
00278      * work as well; the result string has "can't get timezone" for ECMA-valid
00279      * years.  Might still make sense to use this, but find the range of years
00280      * for which valid tz information exists, and map (per ECMA hint) from the
00281      * given year into that range.
00282 
00283      * N.B. This hasn't been tested with anything that actually _uses_
00284      * tm_gmtoff; zero might be the wrong thing to set it to if you really need
00285      * to format a time.  This fix is for jsdate.c, which only uses
00286      * JS_FormatTime to get a string representing the time zone.  */
00287     memset(&a, 0, sizeof(struct tm));
00288 
00289     a.tm_sec = prtm->tm_sec;
00290     a.tm_min = prtm->tm_min;
00291     a.tm_hour = prtm->tm_hour;
00292     a.tm_mday = prtm->tm_mday;
00293     a.tm_mon = prtm->tm_mon;
00294     a.tm_wday = prtm->tm_wday;
00295     a.tm_year = prtm->tm_year - 1900;
00296     a.tm_yday = prtm->tm_yday;
00297     a.tm_isdst = prtm->tm_isdst;
00298 
00299     /* Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff
00300      * are null.  This doesn't quite work, though - the timezone is off by
00301      * tzoff + dst.  (And mktime seems to return -1 for the exact dst
00302      * changeover time.)
00303 
00304      */
00305 
00306 #if defined(SUNOS4)
00307     if (mktime(&a) == -1) {
00308         /* Seems to fail whenever the requested date is outside of the 32-bit
00309          * UNIX epoch.  We could proceed at this point (setting a.tm_zone to
00310          * "") but then strftime returns a string with a 2-digit field of
00311          * garbage for the year.  So we return 0 and hope jsdate.c
00312          * will fall back on toString.
00313          */
00314         return 0;
00315     }
00316 #endif
00317 
00318     return strftime(buf, buflen, fmt, &a);
00319 #endif
00320 }
00321 
00322 /* table for number of days in a month */
00323 static int mtab[] = {
00324     /* jan, feb,mar,apr,may,jun */
00325     31,28,31,30,31,30,
00326     /* july,aug,sep,oct,nov,dec */
00327     31,31,30,31,30,31
00328 };
00329 
00330 /*
00331  * basic time calculation functionality for localtime and gmtime
00332  * setups up prtm argument with correct values based upon input number
00333  * of seconds.
00334  */
00335 static void
00336 PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm)
00337 {
00338     /* convert tsecs back to year,month,day,hour,secs */
00339     JSInt32 year    = 0;
00340     JSInt32 month   = 0;
00341     JSInt32 yday    = 0;
00342     JSInt32 mday    = 0;
00343     JSInt32 wday    = 6; /* start on a Sunday */
00344     JSInt32 days    = 0;
00345     JSInt32 seconds = 0;
00346     JSInt32 minutes = 0;
00347     JSInt32 hours   = 0;
00348     JSInt32 isleap  = 0;
00349     JSInt64 result;
00350     JSInt64   result1;
00351     JSInt64   result2;
00352     JSInt64 base;
00353 
00354     JSLL_UI2L(result,0);
00355     JSLL_UI2L(result1,0);
00356     JSLL_UI2L(result2,0);
00357 
00358     /* get the base time via UTC */
00359     base = PRMJ_ToExtendedTime(0);
00360     JSLL_UI2L(result,  PRMJ_USEC_PER_SEC);
00361     JSLL_DIV(base,base,result);
00362     JSLL_ADD(tsecs,tsecs,base);
00363 
00364     JSLL_UI2L(result, PRMJ_YEAR_SECONDS);
00365     JSLL_UI2L(result1,PRMJ_DAY_SECONDS);
00366     JSLL_ADD(result2,result,result1);
00367 
00368     /* get the year */
00369     while ((isleap == 0) ? !JSLL_CMP(tsecs,<,result) : !JSLL_CMP(tsecs,<,result2)) {
00370         /* subtract a year from tsecs */
00371         JSLL_SUB(tsecs,tsecs,result);
00372         days += 365;
00373         /* is it a leap year ? */
00374         if(IS_LEAP(year)){
00375             JSLL_SUB(tsecs,tsecs,result1);
00376             days++;
00377         }
00378         year++;
00379         isleap = IS_LEAP(year);
00380     }
00381 
00382     JSLL_UI2L(result1,PRMJ_DAY_SECONDS);
00383 
00384     JSLL_DIV(result,tsecs,result1);
00385     JSLL_L2I(mday,result);
00386 
00387     /* let's find the month */
00388     while(((month == 1 && isleap) ?
00389             (mday >= mtab[month] + 1) :
00390             (mday >= mtab[month]))){
00391         yday += mtab[month];
00392         days += mtab[month];
00393 
00394         mday -= mtab[month];
00395 
00396          /* it's a Feb, check if this is a leap year */
00397         if(month == 1 && isleap != 0){
00398             yday++;
00399             days++;
00400             mday--;
00401         }
00402         month++;
00403     }
00404 
00405     /* now adjust tsecs */
00406     JSLL_MUL(result,result,result1);
00407     JSLL_SUB(tsecs,tsecs,result);
00408 
00409     mday++; /* day of month always start with 1 */
00410     days += mday;
00411     wday = (days + wday) % 7;
00412 
00413     yday += mday;
00414 
00415     /* get the hours */
00416     JSLL_UI2L(result1,PRMJ_HOUR_SECONDS);
00417     JSLL_DIV(result,tsecs,result1);
00418     JSLL_L2I(hours,result);
00419     JSLL_MUL(result,result,result1);
00420     JSLL_SUB(tsecs,tsecs,result);
00421 
00422     /* get minutes */
00423     JSLL_UI2L(result1,60);
00424     JSLL_DIV(result,tsecs,result1);
00425     JSLL_L2I(minutes,result);
00426     JSLL_MUL(result,result,result1);
00427     JSLL_SUB(tsecs,tsecs,result);
00428 
00429     JSLL_L2I(seconds,tsecs);
00430 
00431     prtm->tm_usec  = 0L;
00432     prtm->tm_sec   = (JSInt8)seconds;
00433     prtm->tm_min   = (JSInt8)minutes;
00434     prtm->tm_hour  = (JSInt8)hours;
00435     prtm->tm_mday  = (JSInt8)mday;
00436     prtm->tm_mon   = (JSInt8)month;
00437     prtm->tm_wday  = (JSInt8)wday;
00438     prtm->tm_year  = (JSInt16)year;
00439     prtm->tm_yday  = (JSInt16)yday;
00440 }