| 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 *)<ime,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(<ime) - (24 * 3600); | |
| 103 | #else | |
| 104 | 34 | return mktime(<ime) - (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; |