The R Project SVN R

Rev

Rev 27975 | Rev 29378 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
10916 ripley 1
/*
2
 *  R : A Computer Language for Statistical Data Analysis
27975 ripley 3
 *  Copyright (C) 2000-2004  The R Development Core Team.
10916 ripley 4
 *
5
 *  This program is free software; you can redistribute it and/or modify
6
 *  it under the terms of the GNU General Public License as published by
7
 *  the Free Software Foundation; either version 2 of the License, or
8
 *  (at your option) any later version.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU General Public License
16
 *  along with this program; if not, write to the Free Software
17
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 *
19
 *
20
 *      Interfaces to POSIX date and time functions.
21
 */
22
 
23
/*
24
    These use POSIX functions that are not available on all platforms,
25
    and where they are they may be partially or incorrectly implemented.
26
    A number of lightweight alternatives are supplied, but generally
27
    timezone support is only available if the OS supplies it.
28
 
29
    A particular problem is the setting of the timezone TZ on Unix/Linux.
30
    POSIX appears to require it, yet many Linux systems do not set it
27974 ripley 31
    and do not give the correct results/crash strftime if it is not set
32
    (or even if it is: see the workaround below).
10916 ripley 33
    We use unsetenv() to work around this: that is a BSD construct but
34
    seems to be available on the affected platforms.
35
 */
36
 
10543 ripley 37
#ifdef HAVE_CONFIG_H
10819 hornik 38
# include <config.h>
10543 ripley 39
#endif
10819 hornik 40
 
12256 pd 41
#if defined(HAVE_GLIBC2) && !defined(__USE_BSD)
42
# define __USE_BSD		/* so that we get unsetenv() */
43
# include <stdlib.h>
27975 ripley 44
# undef __USE_BSD		/* used later */
12256 pd 45
#else
46
# include <stdlib.h>
47
#endif
48
 
27298 ripley 49
#if defined(HAVE_WORKING_STRPTIME) && defined(HAVE_GLIBC2) && !defined(__USE_XOPEN)
12256 pd 50
# define __USE_XOPEN		/* so that we get strptime() */
10819 hornik 51
# include <time.h>
12256 pd 52
# undef __USE_XOPEN		/* just to make sure */
10872 ripley 53
#else
54
# include <time.h>
10819 hornik 55
#endif
56
 
10543 ripley 57
#include "Defn.h"
58
 
22594 ripley 59
/* The glibc in RH8.0 is broken and assumes that dates before 1970-01-01
60
   do not exist. So does Windows, but at least there we do not need a
22614 ripley 61
   run-time test.  As from 1.6.2, test the actual mktime code and cache the
62
   result on glibc >= 2.2. (It seems this started between 2.2.5 and 2.3,
63
   and RH8.0 has an unreleased version in that gap.)
64
*/
22594 ripley 65
 
66
static Rboolean have_broken_mktime(void)
67
{
68
#ifdef Win32
69
    return TRUE;
70
#elif defined(__GLIBC__) && defined(__GLIBC_MINOR__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 2
22614 ripley 71
    static int test_result = -1;
72
 
73
    if (test_result == -1) {
74
	struct tm t;
75
	time_t res;
76
	t.tm_sec = t.tm_min = t.tm_hour = 0;
77
	t.tm_mday = t.tm_mon = 1;
78
	t.tm_year = 68;
79
	t.tm_isdst = -1;
80
	res = mktime(&t);
81
	test_result = (res == (time_t)-1);
82
    }
83
    return test_result > 0;
22594 ripley 84
#else
85
    return FALSE;
86
#endif
87
 
88
 
89
}
90
 
15252 hornik 91
#ifndef HAVE_WORKING_STRPTIME
10543 ripley 92
/* Substitute based on glibc code. */
10819 hornik 93
# include "Rstrptime.h"
10543 ripley 94
#endif
95
 
96
static const int days_in_month[12] =
97
{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
98
 
99
#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
100
#define days_in_year(year) (isleap(year) ? 366 : 365)
101
 
15332 hornik 102
#ifndef HAVE_POSIX_LEAPSECONDS
10543 ripley 103
static const time_t leapseconds[] =
104
{  78796800, 94694400,126230400,157766400,189302400,220924800,252460800,
105
  283996800,315532800,362793600,425865600,489024000,520560000,567993600,
106
  631152000,662688000,709948800,741484800,773020800,820454400,867715200,
107
  915148800 };
108
#endif
109
 
110
/*
10819 hornik 111
  Adjust a struct tm to be a valid date-time.
112
  Return 0 if valid, -1 if invalid and uncorrectable, or a positive
113
  integer approximating the number of corrections needed.
114
  */
10543 ripley 115
static int validate_tm (struct tm *tm)
116
{
117
    int tmp, res = 0;
118
 
119
    if (tm->tm_sec < 0 || tm->tm_sec > 60) { /* 61 POSIX, 60 draft ISO C */
120
	res++;
121
	tmp = tm->tm_sec/60;
122
	tm->tm_sec -= 60 * tmp; tm->tm_min += tmp;
123
	if(tm->tm_sec < 0) {tm->tm_sec += 60; tm->tm_min--;}
124
    }
125
 
126
    if (tm->tm_min < 0 || tm->tm_min > 59) {
127
	res++;
128
	tmp = tm->tm_min/60;
129
	tm->tm_min -= 60 * tmp; tm->tm_hour += tmp;
130
	if(tm->tm_min < 0) {tm->tm_min += 60; tm->tm_hour--;}
131
    }
132
 
133
    if (tm->tm_hour < 0 || tm->tm_hour > 23) {
134
	res++;
135
	tmp = tm->tm_hour/24;
136
	tm->tm_hour -= 24 * tmp; tm->tm_mday += tmp;
137
	if(tm->tm_hour < 0) {tm->tm_hour += 24; tm->tm_mday--;}
138
    }
139
 
140
    /* defer fixing mday until we know the year */
141
    if (tm->tm_mon < 0 || tm->tm_mon > 11) {
142
	res++;
143
	tmp = tm->tm_mon/12;
144
	tm->tm_mon -= 12 * tmp; tm->tm_year += tmp;
145
	if(tm->tm_mon < 0) {tm->tm_mon += 12; tm->tm_year--;}
146
    }
147
 
27909 ripley 148
    /* A limit on the loops of about 3000x round */
149
    if(tm->tm_mday < -1000000 || tm->tm_mday > 1000000) return -1;
10543 ripley 150
 
27909 ripley 151
    if(abs(tm->tm_mday) > 366) {
152
	res++;
153
	/* first spin back until January */
154
	while(tm->tm_mon > 0) {
155
	    --tm->tm_mon;
156
	    tm->tm_mday += days_in_month[tm->tm_mon] +
157
	    ((tm->tm_mon==1 && isleap(1900+tm->tm_year))? 1 : 0);
158
	}
159
	/* then spin on/back by years */
160
	while(tm->tm_mday < 1) {
161
	    --tm->tm_year;
162
	    tm->tm_mday += 365 + (isleap(1900+tm->tm_year)? 1 : 0);
163
	}
164
	while(tm->tm_mday >
165
	      (tmp = 365 + (isleap(1900+tm->tm_year)? 1 : 0))) {
166
	    tm->tm_mday -= tmp; tm->tm_year++;
167
	}
168
    }
10543 ripley 169
 
10872 ripley 170
    while(tm->tm_mday < 1) {
10543 ripley 171
	res++;
172
	if(--tm->tm_mon < 0) {tm->tm_mon += 12; tm->tm_year--;}
173
	tm->tm_mday += days_in_month[tm->tm_mon] +
174
	    ((tm->tm_mon==1 && isleap(1900+tm->tm_year))? 1 : 0);
175
    }
176
 
177
    while(tm->tm_mday >
178
	  (tmp = days_in_month[tm->tm_mon] +
179
	   ((tm->tm_mon==1 && isleap(1900+tm->tm_year))? 1 : 0))) {
180
	res++;
181
	if(++tm->tm_mon > 11) {tm->tm_mon -= 12; tm->tm_year++;}
182
	tm->tm_mday -= tmp;
183
    }
184
    return res;
185
}
186
 
187
 
188
/* Substitute for mktime -- no checking, always in GMT */
189
static double mktime00 (struct tm *tm)
190
{
191
    long day = 0;
192
    int i, year, year0;
193
 
194
    day = tm->tm_mday - 1;
195
    year0 = 1900 + tm->tm_year;
12778 pd 196
    /* safety check for unbounded loops */
197
    if (abs(year0 - 1970) > 5000) return (double)(-1);
198
 
10543 ripley 199
    for(i = 0; i < tm->tm_mon; i++) day += days_in_month[i];
200
    if (tm->tm_mon > 1 && isleap(year0)) day++;
201
    tm->tm_yday = day;
202
 
203
    if (year0 > 1970) {
204
	for (year = 1970; year < year0; year++)
205
	    day += days_in_year(year);
206
    } else if (year0 < 1970) {
207
	for (year = 1969; year >= year0; year--)
208
	    day -= days_in_year(year);
209
    }
210
 
211
    /* weekday: Epoch day was a Thursday */
212
    if ((tm->tm_wday = (day + 4) % 7) < 0) tm->tm_wday += 7;
213
 
214
    return tm->tm_sec + (tm->tm_min * 60) + (tm->tm_hour * 3600)
215
	+ (day * 86400.0);
216
}
217
 
218
static double guess_offset (struct tm *tm)
219
{
220
    double offset, offset1, offset2;
25845 ripley 221
    int oldmonth, oldyear, olddst, oldwday, oldyday;
10543 ripley 222
 
223
    /*
224
       adjust as best we can for timezones: if isdst is unknown,
225
       use the smaller offset at same day in Jan or July 2000
226
    */
227
    oldmonth = tm->tm_mon;
228
    oldyear = tm->tm_year;
12256 pd 229
    olddst = tm->tm_isdst;
25845 ripley 230
    oldwday = tm->tm_wday;
231
    oldyday = tm->tm_yday;
10543 ripley 232
    tm->tm_mon = 0;
233
    tm->tm_year = 100;
12256 pd 234
    tm->tm_isdst = -1;
10543 ripley 235
    offset1 = (double) mktime(tm) - mktime00(tm);
12256 pd 236
    tm->tm_year = 100;
10543 ripley 237
    tm->tm_mon = 6;
12256 pd 238
    tm->tm_isdst = -1;
10543 ripley 239
    offset2 = (double) mktime(tm) - mktime00(tm);
12256 pd 240
    if(olddst > 0) {
241
	offset = (offset1 > offset2) ? offset2 : offset1;
242
    } else {
10543 ripley 243
	offset = (offset1 > offset2) ? offset1 : offset2;
244
    }
12256 pd 245
    /* now try to guess dst if unknown */
246
    tm->tm_mon = oldmonth;
247
    tm->tm_isdst = -1;
248
    if(olddst < 0) {
249
	offset1 = (double) mktime(tm) - mktime00(tm);
250
	olddst = (offset1 < offset) ? 1:0;
23264 ripley 251
	if(olddst) offset = offset1;
12256 pd 252
    }
10543 ripley 253
    tm->tm_year = oldyear;
12256 pd 254
    tm->tm_isdst = olddst;
25845 ripley 255
    tm->tm_wday = oldwday;
256
    tm->tm_yday = oldyday;
10543 ripley 257
    return offset;
258
}
259
 
260
/* Interface to mktime or mktime00 */
12778 pd 261
static double mktime0 (struct tm *tm, const int local)
10543 ripley 262
{
263
    double res;
15332 hornik 264
#ifndef HAVE_POSIX_LEAPSECONDS
10543 ripley 265
    int i;
266
#endif
267
 
268
    if(validate_tm(tm) < 0) return (double)(-1);
12778 pd 269
    if(!local) return mktime00(tm);
10543 ripley 270
 
271
    if(tm->tm_year < 138 &&
22594 ripley 272
       tm->tm_year >= (have_broken_mktime() ? 70 : 02))
10543 ripley 273
    {   res = (double) mktime(tm);
15332 hornik 274
#ifndef HAVE_POSIX_LEAPSECONDS
10543 ripley 275
        for(i = 0; i < 22; i++)
276
            if(res > leapseconds[i]) res -= 1.0;
277
#endif
278
        return res;
279
/* watch the side effect here: both calls alter their arg */
280
    } else return guess_offset(tm) + mktime00(tm);
281
}
282
 
283
/* Interface for localtime or gmtime or internal substitute */
19912 duncan 284
static struct tm * localtime0(const double *tp, const int local, struct tm *ltm)
10543 ripley 285
{
286
    double d = *tp;
287
    long day;
288
    int y, tmp, mon, left, diff;
19912 duncan 289
    struct tm *res= ltm;
10543 ripley 290
    time_t t;
291
 
22594 ripley 292
    if(d < 2147483647.0 && d > (have_broken_mktime() ? 0. : -2147483647.0)) {
10543 ripley 293
	t = (time_t) d;
15332 hornik 294
#ifndef HAVE_POSIX_LEAPSECONDS
10543 ripley 295
        for(y = 0; y < 22; y++) if(t > leapseconds[y] + y - 1) t++;
296
#endif
297
	return local ? localtime(&t) : gmtime(&t);
298
    }
299
 
300
    day = (long) floor(d/86400.0);
25845 ripley 301
    left = (int) (d - day * 86400.0 + 0.5);
10543 ripley 302
 
303
    /* hour, min, and sec */
304
    res->tm_hour = left / 3600;
305
    left %= 3600;
306
    res->tm_min = left / 60;
307
    res->tm_sec = left % 60;
308
 
309
    /* weekday: 1970-01-01 was a Thursday */
310
    if ((res->tm_wday = ((4 + day) % 7)) < 0) res->tm_wday += 7;
311
 
312
    /* year & day within year */
313
    y = 1970;
314
    if (day >= 0)
315
	for ( ; day >= (tmp = days_in_year(y)); day -= tmp, y++);
316
    else
317
	for ( ; day < 0; --y, day += days_in_year(y) );
318
 
319
    y = res->tm_year = y - 1900;
320
    res->tm_yday = day;
321
 
322
    /* month within year */
323
    for (mon = 0;
27157 ripley 324
	 day >= (tmp = (days_in_month[mon]) + ((mon==1 && isleap(y+1900))?1:0));
10543 ripley 325
	 day -= tmp, mon++);
326
    res->tm_mon = mon;
327
    res->tm_mday = day + 1;
328
 
329
    if(local) {
330
	/*  daylight saving time is unknown */
331
	res->tm_isdst = -1;
332
 
333
	/* Try to fix up timezone differences */
334
        diff = guess_offset(res);
335
	res->tm_min -= diff/60;
336
	validate_tm(res);
337
	return res;
338
    } else {
339
	res->tm_isdst = 0; /* no dst in GMT */
340
	return res;
341
    }
342
}
343
 
344
 
345
 
346
SEXP do_systime(SEXP call, SEXP op, SEXP args, SEXP env)
347
{
348
    time_t res = time(NULL);
13792 pd 349
    SEXP ans = allocVector(REALSXP, 1);
15332 hornik 350
#ifndef HAVE_POSIX_LEAPSECONDS
10543 ripley 351
    res -= 22;
352
#endif
353
    if(res != (time_t)(-1)) REAL(ans)[0] = (double) res;
354
    else REAL(ans)[0] = NA_REAL;
355
    return ans;
356
}
357
 
12778 pd 358
 
15640 ripley 359
#ifdef Win32
10543 ripley 360
#define tzname _tzname
24068 ripley 361
#else /* Unix */
10543 ripley 362
extern char *tzname[2];
363
#endif
364
 
12778 pd 365
static int set_tz(char *tz, char *oldtz)
366
{
367
    char *p = NULL;
368
    int settz = 0;
23492 ripley 369
    static char buff[200];
16537 ripley 370
 
12778 pd 371
    strcpy(oldtz, "");
372
    p = getenv("TZ");
373
    if(p) strcpy(oldtz, p);
374
#ifdef HAVE_PUTENV
375
    strcpy(buff, "TZ="); strcat(buff, tz);
376
    putenv(buff);
377
    settz = 1;
378
#else
379
# ifdef HAVE_SETENV
380
    setenv("TZ", tz, 1);
381
    settz = 1;
382
# else
383
    warning("cannot set timezones on this system");
384
# endif
385
#endif
386
    tzset();
387
    return settz;
388
}
389
 
390
static void reset_tz(char *tz)
391
{
392
    if(strlen(tz)) {
393
#ifdef HAVE_PUTENV
23492 ripley 394
        static char buff[200];
12778 pd 395
	strcpy(buff, "TZ="); strcat(buff, tz);
396
	putenv(buff);
397
#else
398
# ifdef HAVE_SETENV
399
	setenv("TZ", tz, 1);
400
# endif
401
#endif
402
    } else {
403
#ifdef HAVE_UNSETENV
404
	unsetenv("TZ");
405
#else
406
# ifdef HAVE_PUTENV
407
	putenv("TZ=");
408
# endif
409
#endif
410
    }
411
    tzset();
412
}
413
 
414
 
19912 duncan 415
static const char ltnames [][6] =
10543 ripley 416
{ "sec", "min", "hour", "mday", "mon", "year", "wday", "yday", "isdst" };
417
 
418
 
419
static void makelt(struct tm *tm, SEXP ans, int i, int valid)
420
{
421
    int j;
422
 
423
    if(valid) {
424
	INTEGER(VECTOR_ELT(ans, 0))[i] = tm->tm_sec;
425
	INTEGER(VECTOR_ELT(ans, 1))[i] = tm->tm_min;
426
	INTEGER(VECTOR_ELT(ans, 2))[i] = tm->tm_hour;
427
	INTEGER(VECTOR_ELT(ans, 3))[i] = tm->tm_mday;
428
	INTEGER(VECTOR_ELT(ans, 4))[i] = tm->tm_mon;
429
	INTEGER(VECTOR_ELT(ans, 5))[i] = tm->tm_year;
430
	INTEGER(VECTOR_ELT(ans, 6))[i] = tm->tm_wday;
431
	INTEGER(VECTOR_ELT(ans, 7))[i] = tm->tm_yday;
432
	INTEGER(VECTOR_ELT(ans, 8))[i] = tm->tm_isdst;
433
    } else {
434
	for(j = 0; j < 8; j++)
435
	    INTEGER(VECTOR_ELT(ans, j))[i] = NA_INTEGER;
436
	INTEGER(VECTOR_ELT(ans, 8))[i] = -1;
437
    }
438
}
439
 
440
 
441
SEXP do_asPOSIXlt(SEXP call, SEXP op, SEXP args, SEXP env)
442
{
443
    SEXP stz, x, ans, ansnames, class, tzone;
12778 pd 444
    int i, n, isgmt = 0, valid, settz = 0;
445
    char *tz = NULL, oldtz[20] = "";
10543 ripley 446
 
447
    checkArity(op, args);
448
    PROTECT(x = coerceVector(CAR(args), REALSXP));
449
    if(!isString((stz = CADR(args))) || LENGTH(stz) != 1)
450
	error("invalid `tz' value");
451
    tz = CHAR(STRING_ELT(stz, 0));
452
    if(strcmp(tz, "GMT") == 0  || strcmp(tz, "UTC") == 0) isgmt = 1;
12778 pd 453
    if(!isgmt && strlen(tz) > 0) settz = set_tz(tz, oldtz);
10543 ripley 454
 
455
    n = LENGTH(x);
456
    PROTECT(ans = allocVector(VECSXP, 9));
457
    for(i = 0; i < 9; i++)
458
	SET_VECTOR_ELT(ans, i, allocVector(INTSXP, n));
459
 
460
    PROTECT(ansnames = allocVector(STRSXP, 9));
461
    for(i = 0; i < 9; i++)
462
	SET_STRING_ELT(ansnames, i, mkChar(ltnames[i]));
463
 
464
    for(i = 0; i < n; i++) {
19912 duncan 465
        struct tm dummy, *ptm = &dummy;
10543 ripley 466
	if(R_FINITE(REAL(x)[i])){
467
	    double d = REAL(x)[i];
19912 duncan 468
	    ptm = localtime0(&d, 1 - isgmt, &dummy);
10543 ripley 469
	    /* in theory localtime/gmtime always return a valid
470
	       struct tm pointer, but Windows uses NULL for error
471
	       conditions (like negative times). */
472
	    valid = (ptm != NULL);
473
	} else valid = 0;
474
	makelt(ptm, ans, i, valid);
475
    }
476
    setAttrib(ans, R_NamesSymbol, ansnames);
12995 ripley 477
    PROTECT(class = allocVector(STRSXP, 2));
478
    SET_STRING_ELT(class, 0, mkChar("POSIXt"));
479
    SET_STRING_ELT(class, 1, mkChar("POSIXlt"));
10543 ripley 480
    classgets(ans, class);
12778 pd 481
    if (isgmt) {
482
	PROTECT(tzone = allocVector(STRSXP, 1));
483
	SET_STRING_ELT(tzone, 0, mkChar(tz));
484
    } else {
485
	PROTECT(tzone = allocVector(STRSXP, 3));
486
	SET_STRING_ELT(tzone, 0, mkChar(tz));
487
	SET_STRING_ELT(tzone, 1, mkChar(tzname[0]));
488
	SET_STRING_ELT(tzone, 2, mkChar(tzname[1]));
489
    }
10543 ripley 490
    setAttrib(ans, install("tzone"), tzone);
491
    UNPROTECT(5);
492
 
12778 pd 493
    if(settz) reset_tz(oldtz);
10543 ripley 494
    return ans;
495
}
496
 
497
SEXP do_asPOSIXct(SEXP call, SEXP op, SEXP args, SEXP env)
498
{
499
    SEXP stz, x, ans;
10916 ripley 500
    int i, n = 0, isgmt = 0, nlen[9], settz = 0;
12778 pd 501
    char *tz = NULL, oldtz[20] = "";
10543 ripley 502
    struct tm tm;
12778 pd 503
    double tmp;
10543 ripley 504
 
505
    checkArity(op, args);
506
    x = CAR(args);
507
    if(!isVectorList(x) || LENGTH(x) != 9)
508
	error("invalid `x' argument");
509
    if(!isString((stz = CADR(args))) || LENGTH(stz) != 1)
510
	error("invalid `tz' value");
511
 
512
    tz = CHAR(STRING_ELT(stz, 0));
513
    if(strcmp(tz, "GMT") == 0  || strcmp(tz, "UTC") == 0) isgmt = 1;
12778 pd 514
    if(!isgmt && strlen(tz) > 0) settz = set_tz(tz, oldtz);
10543 ripley 515
 
516
    for(i = 0; i < 6; i++)
517
	if((nlen[i] = LENGTH(VECTOR_ELT(x, i))) > n) n = nlen[i];
518
    if((nlen[8] = LENGTH(VECTOR_ELT(x, 8))) > n) n = nlen[8];
519
    if(n > 0) {
520
	for(i = 0; i < 6; i++)
521
	    if(nlen[i] == 0)
522
		error("zero length component in non-empty POSIXlt structure");
523
	if(nlen[8] == 0)
524
	    error("zero length component in non-empty POSIXlt structure");
525
    }
526
    /* coerce fields to integer */
527
    for(i = 0; i < 6; i++)
528
	SET_VECTOR_ELT(x, i, coerceVector(VECTOR_ELT(x, i), INTSXP));
529
    SET_VECTOR_ELT(x, 8, coerceVector(VECTOR_ELT(x, 8), INTSXP));
530
 
531
    PROTECT(ans = allocVector(REALSXP, n));
532
    for(i = 0; i < n; i++) {
533
	tm.tm_sec   = INTEGER(VECTOR_ELT(x, 0))[i%nlen[0]];
534
	tm.tm_min   = INTEGER(VECTOR_ELT(x, 1))[i%nlen[1]];
535
	tm.tm_hour  = INTEGER(VECTOR_ELT(x, 2))[i%nlen[2]];
536
	tm.tm_mday  = INTEGER(VECTOR_ELT(x, 3))[i%nlen[3]];
537
	tm.tm_mon   = INTEGER(VECTOR_ELT(x, 4))[i%nlen[4]];
538
	tm.tm_year  = INTEGER(VECTOR_ELT(x, 5))[i%nlen[5]];
539
	/* mktime ignores tm.tm_wday and tm.tm_yday */
540
	tm.tm_isdst = isgmt ? 0:INTEGER(VECTOR_ELT(x, 8))[i%nlen[8]];
541
	if(tm.tm_sec == NA_INTEGER || tm.tm_min == NA_INTEGER ||
542
	   tm.tm_hour == NA_INTEGER || tm.tm_mday == NA_INTEGER ||
543
	   tm.tm_mon == NA_INTEGER || tm.tm_year == NA_INTEGER)
544
	    REAL(ans)[i] = NA_REAL;
12778 pd 545
	else {
546
	    tmp = mktime0(&tm, 1 - isgmt);
547
	    REAL(ans)[i] = (tmp == (double)(-1)) ? NA_REAL : tmp;
10916 ripley 548
	}
10543 ripley 549
    }
10916 ripley 550
 
12778 pd 551
    if(settz) reset_tz(oldtz);
552
 
10543 ripley 553
    UNPROTECT(1);
554
    return ans;
555
}
556
 
557
SEXP do_formatPOSIXlt(SEXP call, SEXP op, SEXP args, SEXP env)
558
{
12256 pd 559
    SEXP x, sformat, ans, tz;
560
    int i, n = 0, m, N, nlen[9], UseTZ;
561
    char buff[300], *p;
10543 ripley 562
    struct tm tm;
563
 
564
    checkArity(op, args);
565
    x = CAR(args);
566
    if(!isVectorList(x) || LENGTH(x) != 9)
567
	error("invalid `x' argument");
568
    if(!isString((sformat = CADR(args))) || LENGTH(sformat) == 0)
569
	error("invalid `format' argument");
570
    m = LENGTH(sformat);
12256 pd 571
    UseTZ = asLogical(CADDR(args));
572
    if(UseTZ == NA_LOGICAL)
573
	error("invalid `usetz' argument");
574
    tz = getAttrib(x, install("tzone"));
10543 ripley 575
 
27974 ripley 576
    /* workaround for glibc bug in strftime */
27975 ripley 577
#if defined HAVE_GLIBC2
27974 ripley 578
#ifdef __USE_BSD
579
    tm.tm_zone = NULL;
580
#else
581
    tm.__tm_zone = NULL;
582
#endif
583
#endif
584
 
10543 ripley 585
    /* coerce fields to integer, find length of longest one */
586
    for(i = 0; i < 9; i++) {
587
	nlen[i] = LENGTH(VECTOR_ELT(x, i));
588
	if(nlen[i] > n) n = nlen[i];
589
	SET_VECTOR_ELT(x, i, coerceVector(VECTOR_ELT(x, i), INTSXP));
590
    }
591
    if(n > 0) N = (m > n) ? m:n; else N = 0;
592
 
593
    PROTECT(ans = allocVector(STRSXP, N));
594
    for(i = 0; i < N; i++) {
595
	tm.tm_sec   = INTEGER(VECTOR_ELT(x, 0))[i%nlen[0]];
596
	tm.tm_min   = INTEGER(VECTOR_ELT(x, 1))[i%nlen[1]];
597
	tm.tm_hour  = INTEGER(VECTOR_ELT(x, 2))[i%nlen[2]];
598
	tm.tm_mday  = INTEGER(VECTOR_ELT(x, 3))[i%nlen[3]];
599
	tm.tm_mon   = INTEGER(VECTOR_ELT(x, 4))[i%nlen[4]];
600
	tm.tm_year  = INTEGER(VECTOR_ELT(x, 5))[i%nlen[5]];
601
	tm.tm_wday  = INTEGER(VECTOR_ELT(x, 6))[i%nlen[6]];
602
	tm.tm_yday  = INTEGER(VECTOR_ELT(x, 7))[i%nlen[7]];
603
	tm.tm_isdst = INTEGER(VECTOR_ELT(x, 8))[i%nlen[8]];
604
	if(tm.tm_sec == NA_INTEGER || tm.tm_min == NA_INTEGER ||
605
	   tm.tm_hour == NA_INTEGER || tm.tm_mday == NA_INTEGER ||
606
	   tm.tm_mon == NA_INTEGER || tm.tm_year == NA_INTEGER) {
607
	    SET_STRING_ELT(ans, i, NA_STRING);
608
	} else {
609
	    if(validate_tm(&tm) < 0) SET_STRING_ELT(ans, i, NA_STRING);
610
	    else {
611
		strftime(buff, 256, CHAR(STRING_ELT(sformat, i%m)), &tm);
12256 pd 612
		if(UseTZ && !isNull(tz)) {
613
		    int i = 0;
614
		    if(LENGTH(tz) == 3) {
615
			if(tm.tm_isdst > 0) i = 2;
616
		        else if(tm.tm_isdst == 0) i = 1;
617
			else i = 0; /* Use base timezone name */
618
		    }
619
		    p = CHAR(STRING_ELT(tz, i));
620
		    if(strlen(p)) {
621
			strcat(buff, " ");
622
			strcat(buff, p);
623
		    }
624
		}
10543 ripley 625
		SET_STRING_ELT(ans, i, mkChar(buff));
626
	    }
627
	}
628
    }
629
    UNPROTECT(1);
630
    return ans;
631
}
632
 
16537 ripley 633
static void glibc_fix(struct tm *tm, int *invalid)
634
{
635
    /* set mon and mday which glibc does not always set.
636
       Use current year/... if none has been specified.
637
 
638
       Specifying mon but not mday nor yday is invalid.
639
    */
640
    time_t t = time(NULL);
641
    struct tm *tm0;
642
    int tmp;
643
#ifndef HAVE_POSIX_LEAPSECONDS
644
    t -= 22;
645
#endif
646
    tm0 = localtime(&t);
647
    if(tm->tm_year == NA_INTEGER) tm->tm_year = tm0->tm_year;
19361 ripley 648
    if(tm->tm_mon != NA_INTEGER && tm->tm_mday != NA_INTEGER) return;
649
    /* at least one of the month and the day of the month is missing */
16537 ripley 650
    if(tm->tm_yday != NA_INTEGER) {
651
	/* since we have yday, let that take precedence over mon/mday */
652
	int yday = tm->tm_yday, mon = 0;
653
	while(yday > (tmp = days_in_month[mon] +
654
		      ((mon==1 && isleap(1900+tm->tm_year))? 1 : 0))) {
655
	    yday -= tmp;
656
	    mon++;
657
	}
658
	tm->tm_mon = mon;
659
	tm->tm_mday = yday + 1;
660
    } else {
661
	if(tm->tm_mday == NA_INTEGER) {
662
	    if(tm->tm_mon != NA_INTEGER) {
663
		*invalid = 1;
664
		return;
665
	    } else tm->tm_mday = tm0->tm_mday;
666
	}
667
	if(tm->tm_mon == NA_INTEGER) tm->tm_mon = tm0->tm_mon;
668
    }
669
}
670
 
671
 
10543 ripley 672
SEXP do_strptime(SEXP call, SEXP op, SEXP args, SEXP env)
673
{
674
    SEXP x, sformat, ans, ansnames, class;
675
    int i, n, m, N, invalid;
676
    struct tm tm;
677
 
678
    checkArity(op, args);
679
    if(!isString((x= CAR(args))))
680
	error("invalid `x' argument");
681
    if(!isString((sformat = CADR(args))) || LENGTH(sformat) == 0)
682
	error("invalid `format' argument");
683
    n = LENGTH(x); m = LENGTH(sformat);
684
    if(n > 0) N = (m > n)?m:n; else N = 0;
685
 
686
    PROTECT(ans = allocVector(VECSXP, 9));
687
    for(i = 0; i < 9; i++)
688
	SET_VECTOR_ELT(ans, i, allocVector(INTSXP, N));
689
 
690
    PROTECT(ansnames = allocVector(STRSXP, 9));
691
    for(i = 0; i < 9; i++)
692
	SET_STRING_ELT(ansnames, i, mkChar(ltnames[i]));
693
 
694
 
695
    for(i = 0; i < N; i++) {
16537 ripley 696
	/* for glibc's sake. That only sets some unspecified fields,
697
	   sometimes. */
698
	tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
699
	tm.tm_year = tm.tm_mon = tm.tm_mday = tm.tm_yday = NA_INTEGER;
10543 ripley 700
	invalid = STRING_ELT(x, i%n) == NA_STRING ||
701
	    !strptime(CHAR(STRING_ELT(x, i%n)),
702
		      CHAR(STRING_ELT(sformat, i%m)), &tm);
27198 ripley 703
	/* it seems glibc cannot valid at all consistently */
10543 ripley 704
	if(!invalid) {
18603 ripley 705
	    /* Solaris sets missing fields to 0 */
18557 ripley 706
	    if(tm.tm_mday == 0) tm.tm_mday = NA_INTEGER;
16537 ripley 707
	    if(tm.tm_mon == NA_INTEGER || tm.tm_mday == NA_INTEGER
708
	       || tm.tm_year == NA_INTEGER)
709
		glibc_fix(&tm, &invalid);
10543 ripley 710
	    tm.tm_isdst = -1;
12778 pd 711
	    mktime0(&tm, 1); /* set wday, yday, isdst */
10543 ripley 712
	}
27198 ripley 713
	invalid = invalid || validate_tm(&tm) != 0;
10543 ripley 714
	makelt(&tm, ans, i, !invalid);
715
    }
716
    setAttrib(ans, R_NamesSymbol, ansnames);
12995 ripley 717
    PROTECT(class = allocVector(STRSXP, 2));
718
    SET_STRING_ELT(class, 0, mkChar("POSIXt"));
719
    SET_STRING_ELT(class, 1, mkChar("POSIXlt"));
10543 ripley 720
    classgets(ans, class);
721
    UNPROTECT(3);
722
    return ans;
723
}
28072 ripley 724
 
725
SEXP do_D2POSIXlt(SEXP call, SEXP op, SEXP args, SEXP env)
726
{
727
    SEXP x, ans, ansnames, class;
728
    int n, i, valid;
729
    long day;
730
    int y, tmp, mon;
731
    struct tm tm;
732
 
733
    checkArity(op, args);
734
    PROTECT(x = coerceVector(CAR(args), REALSXP));
735
    n = LENGTH(x);
736
    PROTECT(ans = allocVector(VECSXP, 9));
737
    for(i = 0; i < 9; i++)
738
	SET_VECTOR_ELT(ans, i, allocVector(INTSXP, n));
739
 
740
    PROTECT(ansnames = allocVector(STRSXP, 9));
741
    for(i = 0; i < 9; i++)
742
	SET_STRING_ELT(ansnames, i, mkChar(ltnames[i]));
743
 
744
    for(i = 0; i < n; i++) {
745
	if(R_FINITE(REAL(x)[i])) {
746
	    day = (long) REAL(x)[i];
747
	    tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
748
	    /* weekday: 1970-01-01 was a Thursday */
749
	    if ((tm.tm_wday = ((4 + day) % 7)) < 0) tm.tm_wday += 7;
750
 
751
	    /* year & day within year */
752
	    y = 1970;
753
	    if (day >= 0)
754
		for ( ; day >= (tmp = days_in_year(y)); day -= tmp, y++);
755
	    else
756
		for ( ; day < 0; --y, day += days_in_year(y) );
757
 
758
	    y = tm.tm_year = y - 1900;
759
	    tm.tm_yday = day;
760
 
761
	    /* month within year */
762
	    for (mon = 0;
763
		 day >= (tmp = (days_in_month[mon]) + 
764
			 ((mon==1 && isleap(y+1900))?1:0));
765
		 day -= tmp, mon++);
766
	    tm.tm_mon = mon;
767
	    tm.tm_mday = day + 1;
768
	    tm.tm_isdst = 0; /* no dst in GMT */
769
 
770
	    valid = 1;
771
	} else valid = 0;
772
	makelt(&tm, ans, i, valid);
773
    }
774
    setAttrib(ans, R_NamesSymbol, ansnames);
775
    PROTECT(class = allocVector(STRSXP, 2));
776
    SET_STRING_ELT(class, 0, mkChar("POSIXt"));
777
    SET_STRING_ELT(class, 1, mkChar("POSIXlt"));
778
    classgets(ans, class);
779
    UNPROTECT(4);
780
 
781
    return ans;
782
}
783
 
784
SEXP do_POSIXlt2D(SEXP call, SEXP op, SEXP args, SEXP env)
785
{
786
    SEXP x, ans, class;
787
    int i, n = 0, nlen[9];
788
    struct tm tm;
789
 
790
    checkArity(op, args);
791
    x = CAR(args);
792
    if(!isVectorList(x) || LENGTH(x) != 9)
793
	error("invalid `x' argument");
794
 
795
    for(i = 3; i < 6; i++)
796
	if((nlen[i] = LENGTH(VECTOR_ELT(x, i))) > n) n = nlen[i];
797
    if((nlen[8] = LENGTH(VECTOR_ELT(x, 8))) > n) n = nlen[8];
798
    if(n > 0) {
799
	for(i = 3; i < 6; i++)
800
	    if(nlen[i] == 0)
801
		error("zero length component in non-empty POSIXlt structure");
802
	if(nlen[8] == 0)
803
	    error("zero length component in non-empty POSIXlt structure");
804
    }
805
    /* coerce fields to integer */
806
    for(i = 0; i < 6; i++)
807
	SET_VECTOR_ELT(x, i, coerceVector(VECTOR_ELT(x, i), INTSXP));
808
    SET_VECTOR_ELT(x, 8, coerceVector(VECTOR_ELT(x, 8), INTSXP));
809
 
810
    PROTECT(ans = allocVector(REALSXP, n));
811
    for(i = 0; i < n; i++) {
812
	tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
813
	tm.tm_mday  = INTEGER(VECTOR_ELT(x, 3))[i%nlen[3]];
814
	tm.tm_mon   = INTEGER(VECTOR_ELT(x, 4))[i%nlen[4]];
815
	tm.tm_year  = INTEGER(VECTOR_ELT(x, 5))[i%nlen[5]];
816
	/* mktime ignores tm.tm_wday and tm.tm_yday */
817
	tm.tm_isdst = 0;
818
	if(tm.tm_mday == NA_INTEGER || tm.tm_mon == NA_INTEGER || 
819
	   tm.tm_year == NA_INTEGER || validate_tm(&tm) < 0)
820
	    REAL(ans)[i] = NA_REAL;
821
	else REAL(ans)[i] = mktime00(&tm)/86400;
822
    }
823
 
824
    PROTECT(class = allocVector(STRSXP, 1));
825
    SET_STRING_ELT(class, 0, mkChar("Date"));
826
    classgets(ans, class);
827
    UNPROTECT(2);
828
    return ans;
829
}