~ubuntu-branches/debian/wheezy/couchdb/wheezy

« back to all changes in this revision

Viewing changes to src/js/prmjtime.c

  • Committer: Bazaar Package Importer
  • Author(s): Noah Slater
  • Date: 2008-02-06 17:03:38 UTC
  • Revision ID: james.westby@ubuntu.com-20080206170338-y411anylx3oplqid
Tags: upstream-0.7.3~svn684
ImportĀ upstreamĀ versionĀ 0.7.3~svn684

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
{
 
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
    memset((char *)&ltime,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(&ltime) - (24 * 3600);
 
103
#else
 
104
    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
{
 
120
    JSInt64 exttime;
 
121
    JSInt64 g1970GMTMicroSeconds;
 
122
    JSInt64 low;
 
123
    JSInt32 diff;
 
124
    JSInt64  tmp;
 
125
    JSInt64  tmp1;
 
126
 
 
127
    diff = PRMJ_LocalGMTDifference();
 
128
    JSLL_UI2L(tmp, PRMJ_USEC_PER_SEC);
 
129
    JSLL_I2L(tmp1,diff);
 
130
    JSLL_MUL(tmp,tmp,tmp1);
 
131
 
 
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);
 
141
 
 
142
    JSLL_I2L(exttime,base_time);
 
143
    JSLL_ADD(exttime,exttime,g1970GMTMicroSeconds);
 
144
    JSLL_SUB(exttime,exttime,tmp);
 
145
    return exttime;
 
146
}
 
147
 
 
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 */
 
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
    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
}
 
213
 
 
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
 
227
 
 
228
 
 
229
    JSLL_UI2L(us2s, PRMJ_USEC_PER_SEC);
 
230
    JSLL_DIV(local_time, local_time, us2s);
 
231
 
 
232
    /* get the maximum of time_t value */
 
233
    JSLL_UI2L(maxtimet,PRMJ_MAX_UNIX_TIMET);
 
234
 
 
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
 
252
 
 
253
    diff = ((tm.tm_hour - prtm.tm_hour) * PRMJ_HOUR_SECONDS) +
 
254
        ((tm.tm_min - prtm.tm_min) * 60);
 
255
 
 
256
    if(diff < 0){
 
257
        diff += PRMJ_DAY_SECONDS;
 
258
    }
 
259
 
 
260
    JSLL_UI2L(local_time,diff);
 
261
 
 
262
    JSLL_MUL(local_time,local_time,us2s);
 
263
 
 
264
    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
{
 
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
    memset(&a, 0, sizeof(struct tm));
 
288
 
 
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;
 
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
    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
{
 
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;
 
353
 
 
354
    JSLL_UI2L(result,0);
 
355
    JSLL_UI2L(result1,0);
 
356
    JSLL_UI2L(result2,0);
 
357
 
 
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);
 
363
 
 
364
    JSLL_UI2L(result, PRMJ_YEAR_SECONDS);
 
365
    JSLL_UI2L(result1,PRMJ_DAY_SECONDS);
 
366
    JSLL_ADD(result2,result,result1);
 
367
 
 
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
    }
 
381
 
 
382
    JSLL_UI2L(result1,PRMJ_DAY_SECONDS);
 
383
 
 
384
    JSLL_DIV(result,tsecs,result1);
 
385
    JSLL_L2I(mday,result);
 
386
 
 
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];
 
393
 
 
394
         mday -= mtab[month];
 
395
 
 
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
    }
 
404
 
 
405
    /* now adjust tsecs */
 
406
    JSLL_MUL(result,result,result1);
 
407
    JSLL_SUB(tsecs,tsecs,result);
 
408
 
 
409
    mday++; /* day of month always start with 1 */
 
410
    days += mday;
 
411
    wday = (days + wday) % 7;
 
412
 
 
413
    yday += mday;
 
414
 
 
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);
 
421
 
 
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);
 
428
 
 
429
    JSLL_L2I(seconds,tsecs);
 
430
 
 
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
}