1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
18 *
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
40 /*
41 * PR time code.
42 */
43 #include "jsstddef.h"
44 #ifdef SOLARIS
45 #define _REENTRANT 1
46 #endif
47 #include <string.h>
48 #include <time.h>
49 #include "jstypes.h"
50 #include "jsutil.h"
52 #include "jsprf.h"
53 #include "prmjtime.h"
55 #define PRMJ_DO_MILLISECONDS 1
57 #ifdef XP_OS2
58 #include <sys/timeb.h>
59 #endif
60 #ifdef XP_WIN
61 #include <windef.h>
62 #include <winbase.h>
63 #endif
65 #if defined(XP_UNIX) || defined(XP_BEOS)
67 #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
68 extern int gettimeofday(struct timeval *tv);
69 #endif
71 #include <sys/time.h>
73 #endif /* XP_UNIX */
75 #define IS_LEAP(year) \
76 (year != 0 && ((((year & 0x3) == 0) && \
77 ((year - ((year/100) * 100)) != 0)) || \
78 (year - ((year/400) * 400)) == 0))
80 #define PRMJ_HOUR_SECONDS 3600L
81 #define PRMJ_DAY_SECONDS (24L * PRMJ_HOUR_SECONDS)
82 #define PRMJ_YEAR_SECONDS (PRMJ_DAY_SECONDS * 365L)
83 #define PRMJ_MAX_UNIX_TIMET 2145859200L /*time_t value equiv. to 12/31/2037 */
84 /* function prototypes */
85 static void PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm);
86 /*
87 * get the difference in seconds between this time zone and UTC (GMT)
88 */
89 JSInt32
90 PRMJ_LocalGMTDifference()
91 {
92 #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
93 struct tm ltime;
95 /* get the difference between this time zone and GMT */
96 memset((char *)<ime,0,sizeof(ltime));
97 ltime.tm_mday = 2;
98 ltime.tm_year = 70;
99 #ifdef SUNOS4
100 ltime.tm_zone = 0;
101 ltime.tm_gmtoff = 0;
102 return timelocal(<ime) - (24 * 3600);
103 #else
104 return mktime(<ime) - (24L * 3600L);
105 #endif
106 #endif
107 }
109 /* Constants for GMT offset from 1970 */
110 #define G1970GMTMICROHI 0x00dcdcad /* micro secs to 1970 hi */
111 #define G1970GMTMICROLOW 0x8b3fa000 /* micro secs to 1970 low */
113 #define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */
114 #define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */
116 /* Convert from base time to extended time */
117 static JSInt64
118 PRMJ_ToExtendedTime(JSInt32 base_time)
119 {
120 JSInt64 exttime;
121 JSInt64 g1970GMTMicroSeconds;
122 JSInt64 low;
123 JSInt32 diff;
124 JSInt64 tmp;
125 JSInt64 tmp1;
127 diff = PRMJ_LocalGMTDifference();
128 JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC);
129 JSLL_I2L(tmp1,diff);
130 JSLL_MUL(tmp,tmp,tmp1);
132 JSLL_UI2L(g1970GMTMicroSeconds,G1970GMTMICROHI);
133 JSLL_UI2L(low,G1970GMTMICROLOW);
134 #ifndef JS_HAVE_LONG_LONG
135 JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16);
136 JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,16);
137 #else
138 JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,32);
139 #endif
140 JSLL_ADD(g1970GMTMicroSeconds,g1970GMTMicroSeconds,low);
142 JSLL_I2L(exttime,base_time);
143 JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds);
144 JSLL_SUB(exttime,exttime,tmp);
145 return exttime;
146 }
148 JSInt64
149 PRMJ_Now(void)
150 {
151 #ifdef XP_OS2
152 JSInt64 s, us, ms2us, s2us;
153 struct timeb b;
154 #endif
155 #ifdef XP_WIN
156 JSInt64 s, us,
157 win2un = JSLL_INIT(0x19DB1DE, 0xD53E8000),
158 ten = JSLL_INIT(0, 10);
159 FILETIME time, midnight;
160 #endif
161 #if defined(XP_UNIX) || defined(XP_BEOS)
162 struct timeval tv;
163 JSInt64 s, us, s2us;
164 #endif /* XP_UNIX */
166 #ifdef XP_OS2
167 ftime(&b);
168 JSLL_UI2L(ms2us, PRMJ_USEC_PER_MSEC);
169 JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC);
170 JSLL_UI2L(s, b.time);
171 JSLL_UI2L(us, b.millitm);
172 JSLL_MUL(us, us, ms2us);
173 JSLL_MUL(s, s, s2us);
174 JSLL_ADD(s, s, us);
175 return s;
176 #endif
177 #ifdef XP_WIN
178 /* The windows epoch is around 1600. The unix epoch is around 1970.
179 win2un is the difference (in windows time units which are 10 times
180 more precise than the JS time unit) */
181 GetSystemTimeAsFileTime(&time);
182 /* Win9x gets confused at midnight
183 http://support.microsoft.com/default.aspx?scid=KB;en-us;q224423
184 So if the low part (precision <8mins) is 0 then we get the time
185 again. */
186 if (!time.dwLowDateTime) {
187 GetSystemTimeAsFileTime(&midnight);
188 time.dwHighDateTime = midnight.dwHighDateTime;
189 }
190 JSLL_UI2L(s, time.dwHighDateTime);
191 JSLL_UI2L(us, time.dwLowDateTime);
192 JSLL_SHL(s, s, 32);
193 JSLL_ADD(s, s, us);
194 JSLL_SUB(s, s, win2un);
195 JSLL_DIV(s, s, ten);
196 return s;
197 #endif
199 #if defined(XP_UNIX) || defined(XP_BEOS)
200 #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
201 gettimeofday(&tv);
202 #else
203 gettimeofday(&tv, 0);
204 #endif /* _SVID_GETTOD */
205 JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC);
206 JSLL_UI2L(s, tv.tv_sec);
207 JSLL_UI2L(us, tv.tv_usec);
208 JSLL_MUL(s, s, s2us);
209 JSLL_ADD(s, s, us);
210 return s;
211 #endif /* XP_UNIX */
212 }
214 /* Get the DST timezone offset for the time passed in */
215 JSInt64
216 PRMJ_DSTOffset(JSInt64 local_time)
217 {
218 JSInt64 us2s;
219 time_t local;
220 JSInt32 diff;
221 JSInt64 maxtimet;
222 struct tm tm;
223 PRMJTime prtm;
224 #ifndef HAVE_LOCALTIME_R
225 struct tm *ptm;
226 #endif
229 JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC);
230 JSLL_DIV(local_time, local_time, us2s);
232 /* get the maximum of time_t value */
233 JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET);
235 if(JSLL_CMP(local_time,>,maxtimet)){
236 JSLL_UI2L(local_time,PRMJ_MAX_UNIX_TIMET);
237 } else if(!JSLL_GE_ZERO(local_time)){
238 /*go ahead a day to make localtime work (does not work with 0) */
239 JSLL_UI2L(local_time,PRMJ_DAY_SECONDS);
240 }
241 JSLL_L2UI(local,local_time);
242 PRMJ_basetime(local_time,&prtm);
243 #ifndef HAVE_LOCALTIME_R
244 ptm = localtime(&local);
245 if(!ptm){
246 return JSLL_ZERO;
247 }
248 tm = *ptm;
249 #else
250 localtime_r(&local,&tm); /* get dst information */
251 #endif
253 diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) +
254 ((tm.tm_min - prtm.tm_min) * 60);
256 if(diff < 0){
257 diff += PRMJ_DAY_SECONDS;
258 }
260 JSLL_UI2L(local_time,diff);
262 JSLL_MUL(local_time,local_time,us2s);
264 return(local_time);
265 }
267 /* Format a time value into a buffer. Same semantics as strftime() */
268 size_t
269 PRMJ_FormatTime(char *buf, int buflen, char *fmt, PRMJTime *prtm)
270 {
271 #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
272 struct tm a;
274 /* Zero out the tm struct. Linux, SunOS 4 struct tm has extra members int
275 * tm_gmtoff, char *tm_zone; when tm_zone is garbage, strftime gets
276 * confused and dumps core. NSPR20 prtime.c attempts to fill these in by
277 * calling mktime on the partially filled struct, but this doesn't seem to
278 * work as well; the result string has "can't get timezone" for ECMA-valid
279 * years. Might still make sense to use this, but find the range of years
280 * for which valid tz information exists, and map (per ECMA hint) from the
281 * given year into that range.
283 * N.B. This hasn't been tested with anything that actually _uses_
284 * tm_gmtoff; zero might be the wrong thing to set it to if you really need
285 * to format a time. This fix is for jsdate.c, which only uses
286 * JS_FormatTime to get a string representing the time zone. */
287 memset(&a, 0, sizeof(struct tm));
289 a.tm_sec = prtm->tm_sec;
290 a.tm_min = prtm->tm_min;
291 a.tm_hour = prtm->tm_hour;
292 a.tm_mday = prtm->tm_mday;
293 a.tm_mon = prtm->tm_mon;
294 a.tm_wday = prtm->tm_wday;
295 a.tm_year = prtm->tm_year - 1900;
296 a.tm_yday = prtm->tm_yday;
297 a.tm_isdst = prtm->tm_isdst;
299 /* Even with the above, SunOS 4 seems to detonate if tm_zone and tm_gmtoff
300 * are null. This doesn't quite work, though - the timezone is off by
301 * tzoff + dst. (And mktime seems to return -1 for the exact dst
302 * changeover time.)
304 */
306 #if defined(SUNOS4)
307 if (mktime(&a) == -1) {
308 /* Seems to fail whenever the requested date is outside of the 32-bit
309 * UNIX epoch. We could proceed at this point (setting a.tm_zone to
310 * "") but then strftime returns a string with a 2-digit field of
311 * garbage for the year. So we return 0 and hope jsdate.c
312 * will fall back on toString.
313 */
314 return 0;
315 }
316 #endif
318 return strftime(buf, buflen, fmt, &a);
319 #endif
320 }
322 /* table for number of days in a month */
323 static int mtab[] = {
324 /* jan, feb,mar,apr,may,jun */
325 31,28,31,30,31,30,
326 /* july,aug,sep,oct,nov,dec */
327 31,31,30,31,30,31
328 };
330 /*
331 * basic time calculation functionality for localtime and gmtime
332 * setups up prtm argument with correct values based upon input number
333 * of seconds.
334 */
335 static void
336 PRMJ_basetime(JSInt64 tsecs, PRMJTime *prtm)
337 {
338 /* convert tsecs back to year,month,day,hour,secs */
339 JSInt32 year = 0;
340 JSInt32 month = 0;
341 JSInt32 yday = 0;
342 JSInt32 mday = 0;
343 JSInt32 wday = 6; /* start on a Sunday */
344 JSInt32 days = 0;
345 JSInt32 seconds = 0;
346 JSInt32 minutes = 0;
347 JSInt32 hours = 0;
348 JSInt32 isleap = 0;
349 JSInt64 result;
350 JSInt64 result1;
351 JSInt64 result2;
352 JSInt64 base;
354 JSLL_UI2L(result,0);
355 JSLL_UI2L(result1,0);
356 JSLL_UI2L(result2,0);
358 /* get the base time via UTC */
359 base = PRMJ_ToExtendedTime(0);
360 JSLL_UI2L(result, PRMJ_USEC_PER_SEC);
361 JSLL_DIV(base,base,result);
362 JSLL_ADD(tsecs,tsecs,base);
364 JSLL_UI2L(result, PRMJ_YEAR_SECONDS);
365 JSLL_UI2L(result1,PRMJ_DAY_SECONDS);
366 JSLL_ADD(result2,result,result1);
368 /* get the year */
369 while ((isleap == 0) ? !JSLL_CMP(tsecs,<,result) : !JSLL_CMP(tsecs,<,result2)) {
370 /* subtract a year from tsecs */
371 JSLL_SUB(tsecs,tsecs,result);
372 days += 365;
373 /* is it a leap year ? */
374 if(IS_LEAP(year)){
375 JSLL_SUB(tsecs,tsecs,result1);
376 days++;
377 }
378 year++;
379 isleap = IS_LEAP(year);
380 }
382 JSLL_UI2L(result1,PRMJ_DAY_SECONDS);
384 JSLL_DIV(result,tsecs,result1);
385 JSLL_L2I(mday,result);
387 /* let's find the month */
388 while(((month == 1 && isleap) ?
389 (mday >= mtab[month] + 1) :
390 (mday >= mtab[month]))){
391 yday += mtab[month];
392 days += mtab[month];
394 mday -= mtab[month];
396 /* it's a Feb, check if this is a leap year */
397 if(month == 1 && isleap != 0){
398 yday++;
399 days++;
400 mday--;
401 }
402 month++;
403 }
405 /* now adjust tsecs */
406 JSLL_MUL(result,result,result1);
407 JSLL_SUB(tsecs,tsecs,result);
409 mday++; /* day of month always start with 1 */
410 days += mday;
411 wday = (days + wday) % 7;
413 yday += mday;
415 /* get the hours */
416 JSLL_UI2L(result1,PRMJ_HOUR_SECONDS);
417 JSLL_DIV(result,tsecs,result1);
418 JSLL_L2I(hours,result);
419 JSLL_MUL(result,result,result1);
420 JSLL_SUB(tsecs,tsecs,result);
422 /* get minutes */
423 JSLL_UI2L(result1,60);
424 JSLL_DIV(result,tsecs,result1);
425 JSLL_L2I(minutes,result);
426 JSLL_MUL(result,result,result1);
427 JSLL_SUB(tsecs,tsecs,result);
429 JSLL_L2I(seconds,tsecs);
431 prtm->tm_usec = 0L;
432 prtm->tm_sec = (JSInt8)seconds;
433 prtm->tm_min = (JSInt8)minutes;
434 prtm->tm_hour = (JSInt8)hours;
435 prtm->tm_mday = (JSInt8)mday;
436 prtm->tm_mon = (JSInt8)month;
437 prtm->tm_wday = (JSInt8)wday;
438 prtm->tm_year = (JSInt16)year;
439 prtm->tm_yday = (JSInt16)yday;
440 }