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 ***** */
39
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"
51
52 #include "jsprf.h"
53 #include "prmjtime.h"
54
55 #define PRMJ_DO_MILLISECONDS 1
56
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
64
65 #if defined(XP_UNIX) || defined(XP_BEOS)
66
67 #ifdef _SVID_GETTOD /* Defined only on Solaris, see Solaris <sys/types.h> */
68 extern int gettimeofday(struct timeval *tv);
69 #endif
70
71 #include <sys/time.h>
72
73 #endif /* XP_UNIX */
74
75 #define IS_LEAP(year) \
76 (year != 0 && ((((year & 0x3) == 0) && \
77 ((year - ((year/100) * 100)) != 0)) || \
78 (year - ((year/400) * 400)) == 0))
79
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 34 {
92 #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
93 struct tm ltime;
94
95 /* get the difference between this time zone and GMT */
96 34 memset((char *)&ltime,0,sizeof(ltime));
97 34 ltime.tm_mday = 2;
98 34 ltime.tm_year = 70;
99 #ifdef SUNOS4
100 ltime.tm_zone = 0;
101 ltime.tm_gmtoff = 0;
102 return timelocal(&ltime) - (24 * 3600);
103 #else
104 34 return mktime(&ltime) - (24L * 3600L);
105 #endif
106 #endif
107 }
108
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 */
112
113 #define G2037GMTMICROHI 0x00e45fab /* micro secs to 2037 high */
114 #define G2037GMTMICROLOW 0x7a238000 /* micro secs to 2037 low */
115
116 /* Convert from base time to extended time */
117 static JSInt64
118 PRMJ_ToExtendedTime(JSInt32 base_time)
119 0 {
120 JSInt64 exttime;
121 JSInt64 g1970GMTMicroSeconds;
122 JSInt64 low;
123 JSInt32 diff;
124 JSInt64 tmp;
125 JSInt64 tmp1;
126
127 0 diff = PRMJ_LocalGMTDifference();
128 0 JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC);
129 0 JSLL_I2L(tmp1,diff);
130 0 JSLL_MUL(tmp,tmp,tmp1);
131
132 0 JSLL_UI2L(g1970GMTMicroSeconds,G1970GMTMICROHI);
133 0 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 0 JSLL_SHL(g1970GMTMicroSeconds,g1970GMTMicroSeconds,32);
139 #endif
140 0 JSLL_ADD(g1970GMTMicroSeconds,g1970GMTMicroSeconds,low);
141
142 0 JSLL_I2L(exttime,base_time);
143 0 JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds);
144 0 JSLL_SUB(exttime,exttime,tmp);
145 0 return exttime;
146 }
147
148 JSInt64
149 PRMJ_Now(void)
150 0 {
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 */
165
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
198
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 0 gettimeofday(&tv, 0);
204 #endif /* _SVID_GETTOD */
205 0 JSLL_UI2L(s2us, PRMJ_USEC_PER_SEC);
206 0 JSLL_UI2L(s, tv.tv_sec);
207 0 JSLL_UI2L(us, tv.tv_usec);
208 0 JSLL_MUL(s, s, s2us);
209 0 JSLL_ADD(s, s, us);
210 0 return s;
211 #endif /* XP_UNIX */
212 }
213
214 /* Get the DST timezone offset for the time passed in */
215 JSInt64
216 PRMJ_DSTOffset(JSInt64 local_time)
217 0 {
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
227
228
229 0 JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC);
230 0 JSLL_DIV(local_time, local_time, us2s);
231
232 /* get the maximum of time_t value */
233 0 JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET);
234
235 0 if(JSLL_CMP(local_time,>,maxtimet)){
236 0 JSLL_UI2L(local_time,PRMJ_MAX_UNIX_TIMET);
237 0 } else if(!JSLL_GE_ZERO(local_time)){
238 /*go ahead a day to make localtime work (does not work with 0) */
239 0 JSLL_UI2L(local_time,PRMJ_DAY_SECONDS);
240 }
241 0 JSLL_L2UI(local,local_time);
242 0 PRMJ_basetime(local_time,&prtm);
243 #ifndef HAVE_LOCALTIME_R
244 0 ptm = localtime(&local);
245 0 if(!ptm){
246 0 return JSLL_ZERO;
247 }
248 0 tm = *ptm;
249 #else
250 localtime_r(&local,&tm); /* get dst information */
251 #endif
252
253 0 diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) +
254 ((tm.tm_min - prtm.tm_min) * 60);
255
256 0 if(diff < 0){
257 0 diff += PRMJ_DAY_SECONDS;
258 }
259
260 0 JSLL_UI2L(local_time,diff);
261
262 0 JSLL_MUL(local_time,local_time,us2s);
263
264 0 return(local_time);
265 }
266
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 0 {
271 #if defined(XP_UNIX) || defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS)
272 struct tm a;
273
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.
282
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 0 memset(&a, 0, sizeof(struct tm));
288
289 0 a.tm_sec = prtm->tm_sec;
290 0 a.tm_min = prtm->tm_min;
291 0 a.tm_hour = prtm->tm_hour;
292 0 a.tm_mday = prtm->tm_mday;
293 0 a.tm_mon = prtm->tm_mon;
294 0 a.tm_wday = prtm->tm_wday;
295 0 a.tm_year = prtm->tm_year - 1900;
296 0 a.tm_yday = prtm->tm_yday;
297 0 a.tm_isdst = prtm->tm_isdst;
298
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.)
303
304 */
305
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
317
318 0 return strftime(buf, buflen, fmt, &a);
319 #endif
320 }
321
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 };
329
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 0 {
338 /* convert tsecs back to year,month,day,hour,secs */
339 0 JSInt32 year = 0;
340 0 JSInt32 month = 0;
341 0 JSInt32 yday = 0;
342 0 JSInt32 mday = 0;
343 0 JSInt32 wday = 6; /* start on a Sunday */
344 0 JSInt32 days = 0;
345 0 JSInt32 seconds = 0;
346 0 JSInt32 minutes = 0;
347 0 JSInt32 hours = 0;
348 0 JSInt32 isleap = 0;
349 JSInt64 result;
350 JSInt64 result1;
351 JSInt64 result2;
352 JSInt64 base;
353
354 0 JSLL_UI2L(result,0);
355 0 JSLL_UI2L(result1,0);
356 0 JSLL_UI2L(result2,0);
357
358 /* get the base time via UTC */
359 0 base = PRMJ_ToExtendedTime(0);
360 0 JSLL_UI2L(result, PRMJ_USEC_PER_SEC);
361 0 JSLL_DIV(base,base,result);
362 0 JSLL_ADD(tsecs,tsecs,base);
363
364 0 JSLL_UI2L(result, PRMJ_YEAR_SECONDS);
365 0 JSLL_UI2L(result1,PRMJ_DAY_SECONDS);
366 0 JSLL_ADD(result2,result,result1);
367
368 /* get the year */
369 0 while ((isleap == 0) ? !JSLL_CMP(tsecs,<,result) : !JSLL_CMP(tsecs,<,result2)) {
370 /* subtract a year from tsecs */
371 0 JSLL_SUB(tsecs,tsecs,result);
372 0 days += 365;
373 /* is it a leap year ? */
374 0 if(IS_LEAP(year)){
375 0 JSLL_SUB(tsecs,tsecs,result1);
376 0 days++;
377 }
378 0 year++;
379 0 isleap = IS_LEAP(year);
380 }
381
382 0 JSLL_UI2L(result1,PRMJ_DAY_SECONDS);
383
384 0 JSLL_DIV(result,tsecs,result1);
385 0 JSLL_L2I(mday,result);
386
387 /* let's find the month */
388 0 while(((month == 1 && isleap) ?
389 (mday >= mtab[month] + 1) :
390 (mday >= mtab[month]))){
391 0 yday += mtab[month];
392 0 days += mtab[month];
393
394 0 mday -= mtab[month];
395
396 /* it's a Feb, check if this is a leap year */
397 0 if(month == 1 && isleap != 0){
398 0 yday++;
399 0 days++;
400 0 mday--;
401 }
402 0 month++;
403 }
404
405 /* now adjust tsecs */
406 0 JSLL_MUL(result,result,result1);
407 0 JSLL_SUB(tsecs,tsecs,result);
408
409 0 mday++; /* day of month always start with 1 */
410 0 days += mday;
411 0 wday = (days + wday) % 7;
412
413 0 yday += mday;
414
415 /* get the hours */
416 0 JSLL_UI2L(result1,PRMJ_HOUR_SECONDS);
417 0 JSLL_DIV(result,tsecs,result1);
418 0 JSLL_L2I(hours,result);
419 0 JSLL_MUL(result,result,result1);
420 0 JSLL_SUB(tsecs,tsecs,result);
421
422 /* get minutes */
423 0 JSLL_UI2L(result1,60);
424 0 JSLL_DIV(result,tsecs,result1);
425 0 JSLL_L2I(minutes,result);
426 0 JSLL_MUL(result,result,result1);
427 0 JSLL_SUB(tsecs,tsecs,result);
428
429 0 JSLL_L2I(seconds,tsecs);
430
431 0 prtm->tm_usec = 0L;
432 0 prtm->tm_sec = (JSInt8)seconds;
433 0 prtm->tm_min = (JSInt8)minutes;
434 0 prtm->tm_hour = (JSInt8)hours;
435 0 prtm->tm_mday = (JSInt8)mday;
436 0 prtm->tm_mon = (JSInt8)month;
437 0 prtm->tm_wday = (JSInt8)wday;
438 0 prtm->tm_year = (JSInt16)year;
439 0 prtm->tm_yday = (JSInt16)yday;