(strptime): Use #define _GNU_SOURCE to get the
[bpt/guile.git] / libguile / stime.c
CommitLineData
f40771d8 1/* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2003, 2004 Free Software Foundation, Inc.
1c299a6b 2 *
73be1d9e
MV
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
1c299a6b 7 *
73be1d9e 8 * This library is distributed in the hope that it will be useful,
0f2d19dd 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
73be1d9e
MV
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
1c299a6b 12 *
73be1d9e
MV
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
1bbd0b84 17
1bbd0b84 18
0f2d19dd
JB
19\f
20
3cf2f52d
RB
21#if HAVE_CONFIG_H
22# include <config.h>
23#endif
24
0f2d19dd 25#include <stdio.h>
e6e2e95a
MD
26#include <errno.h>
27
a0599745
MD
28#include "libguile/_scm.h"
29#include "libguile/feature.h"
30#include "libguile/strings.h"
31#include "libguile/vectors.h"
20e6290e 32
a0599745
MD
33#include "libguile/validate.h"
34#include "libguile/stime.h"
20e6290e 35
0f2d19dd
JB
36#ifdef HAVE_UNISTD_H
37#include <unistd.h>
38#endif
39
40\f
41# ifdef HAVE_SYS_TYPES_H
42# include <sys/types.h>
43# endif
44
a15e6dcc
MV
45#ifdef HAVE_STRING_H
46#include <string.h>
47#endif
48
b1978258
GH
49#ifdef HAVE_SYS_TIMES_H
50# include <sys/times.h>
51#endif
52
53#ifdef HAVE_SYS_TIMEB_H
54# include <sys/timeb.h>
55#endif
0f2d19dd 56
eb7e1603
KR
57#if HAVE_CRT_EXTERNS_H
58#include <crt_externs.h> /* for Darwin _NSGetEnviron */
59#endif
60
b9525b92
GH
61#ifndef tzname /* For SGI. */
62extern char *tzname[]; /* RS6000 and others reject char **tzname. */
63#endif
79dcdf51
MV
64#if defined (__MINGW32__)
65# define tzname _tzname
66#endif
b9525b92 67
4d3bacdd
JB
68#ifdef MISSING_STRPTIME_DECL
69extern char *strptime ();
70#endif
71
0f2d19dd
JB
72#ifdef __STDC__
73# define timet time_t
74#else
75# define timet long
76#endif
77
eb7e1603
KR
78extern char ** environ;
79
80/* On Apple Darwin in a shared library there's no "environ" to access
81 directly, instead the address of that variable must be obtained with
82 _NSGetEnviron(). */
83#if HAVE__NSGETENVIRON && defined (PIC)
84#define environ (*_NSGetEnviron())
85#endif
86
87
0f2d19dd 88#ifdef HAVE_TIMES
0f2d19dd 89static
1c299a6b 90timet mytime()
0f2d19dd
JB
91{
92 struct tms time_buffer;
93 times(&time_buffer);
94 return time_buffer.tms_utime + time_buffer.tms_stime;
95}
96#else
97# ifdef LACK_CLOCK
756414cf 98# define mytime() ((time((timet*)0) - scm_your_base) * SCM_TIME_UNITS_PER_SECOND)
0f2d19dd
JB
99# else
100# define mytime clock
101# endif
102#endif
103
0f2d19dd 104#ifdef HAVE_FTIME
0f2d19dd 105struct timeb scm_your_base = {0};
b450f070
GB
106#else
107timet scm_your_base = 0;
108#endif
1bbd0b84 109
1c299a6b 110SCM_DEFINE (scm_get_internal_real_time, "get-internal-real-time", 0, 0, 0,
1bbd0b84 111 (),
1e6808ea
MG
112 "Return the number of time units since the interpreter was\n"
113 "started.")
1bbd0b84 114#define FUNC_NAME s_scm_get_internal_real_time
0f2d19dd 115{
b450f070 116#ifdef HAVE_FTIME
0f2d19dd 117 struct timeb time_buffer;
55c4d089
JB
118
119 SCM tmp;
120 ftime (&time_buffer);
0f2d19dd 121 time_buffer.time -= scm_your_base.time;
55c4d089
JB
122 tmp = scm_long2num (time_buffer.millitm - scm_your_base.millitm);
123 tmp = scm_sum (tmp,
124 scm_product (SCM_MAKINUM (1000),
125 SCM_MAKINUM (time_buffer.time)));
756414cf 126 return scm_quotient (scm_product (tmp, SCM_MAKINUM (SCM_TIME_UNITS_PER_SECOND)),
55c4d089 127 SCM_MAKINUM (1000));
0f2d19dd 128#else
756414cf 129 return scm_long2num((time((timet*)0) - scm_your_base) * (int)SCM_TIME_UNITS_PER_SECOND);
b450f070 130#endif /* HAVE_FTIME */
0f2d19dd 131}
1bbd0b84
GB
132#undef FUNC_NAME
133
0f2d19dd 134
f25f761d 135#ifdef HAVE_TIMES
1c299a6b 136SCM_DEFINE (scm_times, "times", 0, 0, 0,
1bbd0b84 137 (void),
1e6808ea
MG
138 "Return an object with information about real and processor\n"
139 "time. The following procedures accept such an object as an\n"
140 "argument and return a selected component:\n"
141 "\n"
b380b885
MD
142 "@table @code\n"
143 "@item tms:clock\n"
144 "The current real time, expressed as time units relative to an\n"
145 "arbitrary base.\n"
146 "@item tms:utime\n"
147 "The CPU time units used by the calling process.\n"
148 "@item tms:stime\n"
1e6808ea
MG
149 "The CPU time units used by the system on behalf of the calling\n"
150 "process.\n"
b380b885 151 "@item tms:cutime\n"
1e6808ea
MG
152 "The CPU time units used by terminated child processes of the\n"
153 "calling process, whose status has been collected (e.g., using\n"
154 "@code{waitpid}).\n"
b380b885 155 "@item tms:cstime\n"
1e6808ea 156 "Similarly, the CPU times units used by the system on behalf of\n"
b380b885
MD
157 "terminated child processes.\n"
158 "@end table")
1bbd0b84 159#define FUNC_NAME s_scm_times
6afcd3b2 160{
6afcd3b2
GH
161 struct tms t;
162 clock_t rv;
163
00ffa0e7 164 SCM result = scm_c_make_vector (5, SCM_UNDEFINED);
6afcd3b2
GH
165 rv = times (&t);
166 if (rv == -1)
1bbd0b84 167 SCM_SYSERROR;
34d19ef6
HWN
168 SCM_VECTOR_SET (result, 0, scm_long2num (rv));
169 SCM_VECTOR_SET (result, 1, scm_long2num (t.tms_utime));
170 SCM_VECTOR_SET (result, 2, scm_long2num (t.tms_stime));
171 SCM_VECTOR_SET (result ,3, scm_long2num (t.tms_cutime));
172 SCM_VECTOR_SET (result, 4, scm_long2num (t.tms_cstime));
6afcd3b2 173 return result;
6afcd3b2 174}
1bbd0b84 175#undef FUNC_NAME
f25f761d 176#endif /* HAVE_TIMES */
6afcd3b2 177
0f2d19dd
JB
178static long scm_my_base = 0;
179
1c299a6b
ML
180long
181scm_c_get_internal_run_time ()
182{
183 return mytime () - scm_my_base;
184}
185
186SCM_DEFINE (scm_get_internal_run_time, "get-internal-run-time", 0, 0, 0,
1bbd0b84 187 (void),
1e6808ea
MG
188 "Return the number of time units of processor time used by the\n"
189 "interpreter. Both @emph{system} and @emph{user} time are\n"
190 "included but subprocesses are not.")
1bbd0b84 191#define FUNC_NAME s_scm_get_internal_run_time
0f2d19dd 192{
1c299a6b 193 return scm_long2num (scm_c_get_internal_run_time ());
0f2d19dd 194}
1bbd0b84 195#undef FUNC_NAME
0f2d19dd 196
f40771d8
KR
197/* For reference, note that current-time and gettimeofday both should be
198 protected against setzone/restorezone changes in another thread, since on
199 DOS the system time is normally kept as local time, which means TZ
200 affects the return from current-time and gettimeofday. Not sure if DJGPP
201 etc actually has concurrent multi-threading, but it seems prudent not to
202 make assumptions about this. */
203
1c299a6b 204SCM_DEFINE (scm_current_time, "current-time", 0, 0, 0,
1bbd0b84 205 (void),
1e6808ea
MG
206 "Return the number of seconds since 1970-01-01 00:00:00 UTC,\n"
207 "excluding leap seconds.")
1bbd0b84 208#define FUNC_NAME s_scm_current_time
0f2d19dd 209{
19468eff
GH
210 timet timv;
211
212 SCM_DEFER_INTS;
213 if ((timv = time (0)) == -1)
2d99b584 214 SCM_MISC_ERROR ("current time not available", SCM_EOL);
19468eff
GH
215 SCM_ALLOW_INTS;
216 return scm_long2num((long) timv);
217}
1bbd0b84 218#undef FUNC_NAME
19468eff 219
1c299a6b 220SCM_DEFINE (scm_gettimeofday, "gettimeofday", 0, 0, 0,
1bbd0b84 221 (void),
1e6808ea
MG
222 "Return a pair containing the number of seconds and microseconds\n"
223 "since 1970-01-01 00:00:00 UTC, excluding leap seconds. Note:\n"
224 "whether true microsecond resolution is available depends on the\n"
225 "operating system.")
1bbd0b84 226#define FUNC_NAME s_scm_gettimeofday
19468eff
GH
227{
228#ifdef HAVE_GETTIMEOFDAY
229 struct timeval time;
230
231 SCM_DEFER_INTS;
232 if (gettimeofday (&time, NULL) == -1)
1bbd0b84 233 SCM_SYSERROR;
19468eff
GH
234 SCM_ALLOW_INTS;
235 return scm_cons (scm_long2num ((long) time.tv_sec),
236 scm_long2num ((long) time.tv_usec));
237#else
238# ifdef HAVE_FTIME
239 struct timeb time;
240
241 ftime(&time);
1c299a6b 242 return scm_cons (scm_long2num ((long) time.time),
9a81afca 243 SCM_MAKINUM (time.millitm * 1000));
19468eff
GH
244# else
245 timet timv;
1c299a6b 246
19468eff
GH
247 SCM_DEFER_INTS;
248 if ((timv = time (0)) == -1)
1bbd0b84 249 SCM_SYSERROR;
19468eff
GH
250 SCM_ALLOW_INTS;
251 return scm_cons (scm_long2num (timv), SCM_MAKINUM (0));
252# endif
253#endif
254}
1bbd0b84 255#undef FUNC_NAME
19468eff
GH
256
257static SCM
d6d9e957 258filltime (struct tm *bd_time, int zoff, const char *zname)
19468eff 259{
00ffa0e7 260 SCM result = scm_c_make_vector (11, SCM_UNDEFINED);
19468eff 261
34d19ef6
HWN
262 SCM_VECTOR_SET (result,0, SCM_MAKINUM (bd_time->tm_sec));
263 SCM_VECTOR_SET (result,1, SCM_MAKINUM (bd_time->tm_min));
264 SCM_VECTOR_SET (result,2, SCM_MAKINUM (bd_time->tm_hour));
265 SCM_VECTOR_SET (result,3, SCM_MAKINUM (bd_time->tm_mday));
266 SCM_VECTOR_SET (result,4, SCM_MAKINUM (bd_time->tm_mon));
267 SCM_VECTOR_SET (result,5, SCM_MAKINUM (bd_time->tm_year));
268 SCM_VECTOR_SET (result,6, SCM_MAKINUM (bd_time->tm_wday));
269 SCM_VECTOR_SET (result,7, SCM_MAKINUM (bd_time->tm_yday));
270 SCM_VECTOR_SET (result,8, SCM_MAKINUM (bd_time->tm_isdst));
271 SCM_VECTOR_SET (result,9, SCM_MAKINUM (zoff));
272 SCM_VECTOR_SET (result,10, zname ? scm_makfrom0str (zname) : SCM_BOOL_F);
19468eff
GH
273 return result;
274}
275
ef9ff3fd 276static char tzvar[3] = "TZ";
ef9ff3fd 277
38c1d3c4
GH
278/* if zone is set, create a temporary environment with only a TZ
279 string. other threads or interrupt handlers shouldn't be allowed
280 to run until the corresponding restorezone is called. hence the use
281 of a static variable for tmpenv is no big deal. */
ef9ff3fd 282static char **
3eeba8d4 283setzone (SCM zone, int pos, const char *subr)
b9525b92 284{
ef9ff3fd 285 char **oldenv = 0;
b9525b92
GH
286
287 if (!SCM_UNBNDP (zone))
288 {
ef9ff3fd 289 static char *tmpenv[2];
b9525b92
GH
290 char *buf;
291
9fd38a3d 292 SCM_ASSERT (SCM_STRINGP (zone), zone, pos, subr);
4c9419ac 293 buf = scm_malloc (SCM_STRING_LENGTH (zone) + sizeof (tzvar) + 1);
a6d9e5ab 294 sprintf (buf, "%s=%s", tzvar, SCM_STRING_CHARS (zone));
ef9ff3fd
GH
295 oldenv = environ;
296 tmpenv[0] = buf;
297 tmpenv[1] = 0;
298 environ = tmpenv;
b9525b92 299 }
ef9ff3fd 300 return oldenv;
b9525b92
GH
301}
302
303static void
e81d98ec 304restorezone (SCM zone, char **oldenv, const char *subr SCM_UNUSED)
b9525b92
GH
305{
306 if (!SCM_UNBNDP (zone))
307 {
4c9419ac 308 free (environ[0]);
ef9ff3fd 309 environ = oldenv;
38c1d3c4
GH
310#ifdef HAVE_TZSET
311 /* for the possible benefit of user code linked with libguile. */
b9525b92 312 tzset();
38c1d3c4 313#endif
b9525b92
GH
314 }
315}
316
1c299a6b 317SCM_DEFINE (scm_localtime, "localtime", 1, 1, 0,
1bbd0b84 318 (SCM time, SCM zone),
1e6808ea
MG
319 "Return an object representing the broken down components of\n"
320 "@var{time}, an integer like the one returned by\n"
321 "@code{current-time}. The time zone for the calculation is\n"
322 "optionally specified by @var{zone} (a string), otherwise the\n"
323 "@code{TZ} environment variable or the system default is used.")
1bbd0b84 324#define FUNC_NAME s_scm_localtime
19468eff
GH
325{
326 timet itime;
4edc089c 327 struct tm *ltptr, lt, *utc;
19468eff
GH
328 SCM result;
329 int zoff;
330 char *zname = 0;
ef9ff3fd 331 char **oldenv;
19468eff
GH
332 int err;
333
e4b265d8 334 itime = SCM_NUM2LONG (1, time);
38c1d3c4
GH
335
336 /* deferring interupts is essential since a) setzone may install a temporary
337 environment b) localtime uses a static buffer. */
19468eff 338 SCM_DEFER_INTS;
1bbd0b84 339 oldenv = setzone (zone, SCM_ARG2, FUNC_NAME);
38c1d3c4
GH
340#ifdef LOCALTIME_CACHE
341 tzset ();
342#endif
73ae3b4c
KR
343 /* POSIX says localtime sets errno, but C99 doesn't say that.
344 Give a sensible default value in case localtime doesn't set it. */
345 errno = EINVAL;
4edc089c 346 ltptr = localtime (&itime);
19468eff 347 err = errno;
4edc089c 348 if (ltptr)
19468eff 349 {
4d3bacdd 350 const char *ptr;
ef9ff3fd 351
43ff3170
GH
352 /* copy zone name before calling gmtime or restoring zone. */
353#if defined (HAVE_TM_ZONE)
ef9ff3fd 354 ptr = ltptr->tm_zone;
43ff3170 355#elif defined (HAVE_TZNAME)
ef9ff3fd 356 ptr = tzname[ (ltptr->tm_isdst == 1) ? 1 : 0 ];
43ff3170
GH
357#else
358 ptr = "";
b9525b92 359#endif
4c9419ac 360 zname = scm_malloc (strlen (ptr) + 1);
ef9ff3fd 361 strcpy (zname, ptr);
19468eff 362 }
ef9ff3fd
GH
363 /* the struct is copied in case localtime and gmtime share a buffer. */
364 if (ltptr)
365 lt = *ltptr;
73ae3b4c
KR
366 /* POSIX says gmtime sets errno, but C99 doesn't say that.
367 Give a sensible default value in case gmtime doesn't set it. */
368 errno = EINVAL;
ef9ff3fd
GH
369 utc = gmtime (&itime);
370 if (utc == NULL)
371 err = errno;
1bbd0b84 372 restorezone (zone, oldenv, FUNC_NAME);
b9525b92 373 /* delayed until zone has been restored. */
19468eff 374 errno = err;
4edc089c 375 if (utc == NULL || ltptr == NULL)
1bbd0b84 376 SCM_SYSERROR;
19468eff
GH
377
378 /* calculate timezone offset in seconds west of UTC. */
4edc089c
GH
379 zoff = (utc->tm_hour - lt.tm_hour) * 3600 + (utc->tm_min - lt.tm_min) * 60
380 + utc->tm_sec - lt.tm_sec;
381 if (utc->tm_year < lt.tm_year)
19468eff 382 zoff -= 24 * 60 * 60;
4edc089c 383 else if (utc->tm_year > lt.tm_year)
19468eff 384 zoff += 24 * 60 * 60;
4edc089c 385 else if (utc->tm_yday < lt.tm_yday)
19468eff 386 zoff -= 24 * 60 * 60;
4edc089c 387 else if (utc->tm_yday > lt.tm_yday)
19468eff 388 zoff += 24 * 60 * 60;
1c299a6b 389
4edc089c 390 result = filltime (&lt, zoff, zname);
19468eff 391 SCM_ALLOW_INTS;
4c9419ac
MV
392 if (zname)
393 free (zname);
19468eff
GH
394 return result;
395}
1bbd0b84 396#undef FUNC_NAME
19468eff 397
1c299a6b 398SCM_DEFINE (scm_gmtime, "gmtime", 1, 0, 0,
1bbd0b84 399 (SCM time),
1e6808ea
MG
400 "Return an object representing the broken down components of\n"
401 "@var{time}, an integer like the one returned by\n"
402 "@code{current-time}. The values are calculated for UTC.")
1bbd0b84 403#define FUNC_NAME s_scm_gmtime
19468eff
GH
404{
405 timet itime;
406 struct tm *bd_time;
407 SCM result;
d6d9e957 408 const char *zname;
19468eff 409
e4b265d8 410 itime = SCM_NUM2LONG (1, time);
19468eff 411 SCM_DEFER_INTS;
73ae3b4c
KR
412 /* POSIX says gmtime sets errno, but C99 doesn't say that.
413 Give a sensible default value in case gmtime doesn't set it. */
414 errno = EINVAL;
19468eff
GH
415 bd_time = gmtime (&itime);
416 if (bd_time == NULL)
1bbd0b84 417 SCM_SYSERROR;
d6d9e957
KR
418#if HAVE_STRUCT_TM_TM_ZONE
419 zname = bd_time->tm_zone;
420#else
421 zname = "GMT";
422#endif
423 result = filltime (bd_time, 0, zname);
19468eff
GH
424 SCM_ALLOW_INTS;
425 return result;
426}
1bbd0b84 427#undef FUNC_NAME
19468eff 428
b9525b92
GH
429/* copy time components from a Scheme object to a struct tm. */
430static void
3eeba8d4 431bdtime2c (SCM sbd_time, struct tm *lt, int pos, const char *subr)
19468eff 432{
34d19ef6 433 SCM const *velts;
55a7fc62
GH
434 int i;
435
0c95b57d 436 SCM_ASSERT (SCM_VECTORP (sbd_time)
9fd38a3d 437 && SCM_VECTOR_LENGTH (sbd_time) == 11,
b9525b92 438 sbd_time, pos, subr);
55a7fc62
GH
439 velts = SCM_VELTS (sbd_time);
440 for (i = 0; i < 10; i++)
441 {
442 SCM_ASSERT (SCM_INUMP (velts[i]), sbd_time, pos, subr);
443 }
0c95b57d 444 SCM_ASSERT (SCM_FALSEP (velts[10]) || SCM_STRINGP (velts[10]),
55a7fc62
GH
445 sbd_time, pos, subr);
446
447 lt->tm_sec = SCM_INUM (velts[0]);
448 lt->tm_min = SCM_INUM (velts[1]);
449 lt->tm_hour = SCM_INUM (velts[2]);
450 lt->tm_mday = SCM_INUM (velts[3]);
451 lt->tm_mon = SCM_INUM (velts[4]);
452 lt->tm_year = SCM_INUM (velts[5]);
453 lt->tm_wday = SCM_INUM (velts[6]);
454 lt->tm_yday = SCM_INUM (velts[7]);
455 lt->tm_isdst = SCM_INUM (velts[8]);
c7abe4f3 456#ifdef HAVE_TM_ZONE
55a7fc62
GH
457 lt->tm_gmtoff = SCM_INUM (velts[9]);
458 if (SCM_FALSEP (velts[10]))
459 lt->tm_zone = NULL;
460 else
a002f1a2 461 lt->tm_zone = SCM_STRING_CHARS (velts[10]);
c7abe4f3 462#endif
b9525b92
GH
463}
464
1c299a6b 465SCM_DEFINE (scm_mktime, "mktime", 1, 1, 0,
1bbd0b84 466 (SCM sbd_time, SCM zone),
a3c8b9fc
MD
467 "@var{bd-time} is an object representing broken down time and @code{zone}\n"
468 "is an optional time zone specifier (otherwise the TZ environment variable\n"
469 "or the system default is used).\n\n"
a8eac221 470 "Returns a pair: the car is a corresponding\n"
a3c8b9fc 471 "integer time value like that returned\n"
a8eac221 472 "by @code{current-time}; the cdr is a broken down time object, similar to\n"
a3c8b9fc 473 "as @var{bd-time} but with normalized values.")
1bbd0b84 474#define FUNC_NAME s_scm_mktime
b9525b92
GH
475{
476 timet itime;
477 struct tm lt, *utc;
478 SCM result;
479 int zoff;
480 char *zname = 0;
ef9ff3fd 481 char **oldenv;
b9525b92
GH
482 int err;
483
1bbd0b84 484 bdtime2c (sbd_time, &lt, SCM_ARG1, FUNC_NAME);
19468eff
GH
485
486 SCM_DEFER_INTS;
1bbd0b84 487 oldenv = setzone (zone, SCM_ARG2, FUNC_NAME);
38c1d3c4
GH
488#ifdef LOCALTIME_CACHE
489 tzset ();
490#endif
19468eff 491 itime = mktime (&lt);
73ae3b4c
KR
492 /* POSIX doesn't say mktime sets errno, and on glibc 2.3.2 for instance it
493 doesn't. Force a sensible value for our error message. */
494 err = EINVAL;
19468eff 495
b9525b92
GH
496 if (itime != -1)
497 {
4d3bacdd 498 const char *ptr;
ef9ff3fd 499
43ff3170
GH
500 /* copy zone name before calling gmtime or restoring the zone. */
501#if defined (HAVE_TM_ZONE)
ef9ff3fd 502 ptr = lt.tm_zone;
43ff3170 503#elif defined (HAVE_TZNAME)
ef9ff3fd 504 ptr = tzname[ (lt.tm_isdst == 1) ? 1 : 0 ];
43ff3170
GH
505#else
506 ptr = "";
b9525b92 507#endif
4c9419ac 508 zname = scm_malloc (strlen (ptr) + 1);
ef9ff3fd 509 strcpy (zname, ptr);
b9525b92 510 }
ef9ff3fd
GH
511
512 /* get timezone offset in seconds west of UTC. */
73ae3b4c
KR
513 /* POSIX says gmtime sets errno, but C99 doesn't say that.
514 Give a sensible default value in case gmtime doesn't set it. */
ef9ff3fd
GH
515 utc = gmtime (&itime);
516 if (utc == NULL)
517 err = errno;
518
1bbd0b84 519 restorezone (zone, oldenv, FUNC_NAME);
b9525b92
GH
520 /* delayed until zone has been restored. */
521 errno = err;
522 if (utc == NULL || itime == -1)
1bbd0b84 523 SCM_SYSERROR;
b9525b92 524
19468eff
GH
525 zoff = (utc->tm_hour - lt.tm_hour) * 3600 + (utc->tm_min - lt.tm_min) * 60
526 + utc->tm_sec - lt.tm_sec;
527 if (utc->tm_year < lt.tm_year)
528 zoff -= 24 * 60 * 60;
529 else if (utc->tm_year > lt.tm_year)
530 zoff += 24 * 60 * 60;
531 else if (utc->tm_yday < lt.tm_yday)
532 zoff -= 24 * 60 * 60;
533 else if (utc->tm_yday > lt.tm_yday)
534 zoff += 24 * 60 * 60;
535
19468eff
GH
536 result = scm_cons (scm_long2num ((long) itime),
537 filltime (&lt, zoff, zname));
538 SCM_ALLOW_INTS;
4c9419ac
MV
539 if (zname)
540 free (zname);
19468eff 541 return result;
0f2d19dd 542}
1bbd0b84 543#undef FUNC_NAME
0f2d19dd 544
38c1d3c4 545#ifdef HAVE_TZSET
1c299a6b 546SCM_DEFINE (scm_tzset, "tzset", 0, 0, 0,
1bbd0b84 547 (void),
a3c8b9fc
MD
548 "Initialize the timezone from the TZ environment variable\n"
549 "or the system default. It's not usually necessary to call this procedure\n"
550 "since it's done automatically by other procedures that depend on the\n"
551 "timezone.")
1bbd0b84 552#define FUNC_NAME s_scm_tzset
0f2d19dd 553{
19468eff
GH
554 tzset();
555 return SCM_UNSPECIFIED;
0f2d19dd 556}
1bbd0b84 557#undef FUNC_NAME
38c1d3c4 558#endif /* HAVE_TZSET */
0f2d19dd 559
a1ec6916 560SCM_DEFINE (scm_strftime, "strftime", 2, 0, 0,
1bbd0b84 561 (SCM format, SCM stime),
a3c8b9fc
MD
562 "Formats a time specification @var{time} using @var{template}. @var{time}\n"
563 "is an object with time components in the form returned by @code{localtime}\n"
564 "or @code{gmtime}. @var{template} is a string which can include formatting\n"
565 "specifications introduced by a @code{%} character. The formatting of\n"
566 "month and day names is dependent on the current locale. The value returned\n"
567 "is the formatted string.\n"
568 "@xref{Formatting Date and Time, , , libc, The GNU C Library Reference Manual}.)")
1bbd0b84 569#define FUNC_NAME s_scm_strftime
b9525b92
GH
570{
571 struct tm t;
572
573 char *tbuf;
574 int size = 50;
a15e6dcc 575 char *fmt, *myfmt;
b9525b92 576 int len;
ef9ff3fd 577 SCM result;
b9525b92 578
a6d9e5ab 579 SCM_VALIDATE_STRING (1, format);
1bbd0b84 580 bdtime2c (stime, &t, SCM_ARG2, FUNC_NAME);
b9525b92 581
a6d9e5ab
DH
582 fmt = SCM_STRING_CHARS (format);
583 len = SCM_STRING_LENGTH (format);
b9525b92 584
a15e6dcc
MV
585 /* Ugly hack: strftime can return 0 if its buffer is too small,
586 but some valid time strings (e.g. "%p") can sometimes produce
587 a zero-byte output string! Workaround is to prepend a junk
588 character to the format string, so that valid returns are always
589 nonzero. */
4c9419ac 590 myfmt = scm_malloc (len+2);
a15e6dcc
MV
591 *myfmt = 'x';
592 strncpy(myfmt+1, fmt, len);
593 myfmt[len+1] = 0;
594
4c9419ac 595 tbuf = scm_malloc (size);
b8a1b29b
GH
596 {
597#if !defined (HAVE_TM_ZONE)
598 /* it seems the only way to tell non-GNU versions of strftime what
599 zone to use (for the %Z format) is to set TZ in the
600 environment. interrupts and thread switching must be deferred
601 until TZ is restored. */
602 char **oldenv = NULL;
2e945bcc 603 SCM *velts = (SCM *) SCM_VELTS (stime);
e652b54f 604 int have_zone = 0;
b8a1b29b 605
36284627 606 if (!SCM_FALSEP (velts[10]) && *SCM_STRING_CHARS (velts[10]) != 0)
b8a1b29b
GH
607 {
608 /* it's not required that the TZ setting be correct, just that
e652b54f
GH
609 it has the right name. so try something like TZ=EST0.
610 using only TZ=EST would be simpler but it doesn't work on
611 some OSs, e.g., Solaris. */
612 SCM zone =
613 scm_string_append (scm_cons (velts[10],
614 scm_cons (scm_makfrom0str ("0"),
615 SCM_EOL)));
1c299a6b 616
e652b54f 617 have_zone = 1;
b8a1b29b 618 SCM_DEFER_INTS;
e652b54f 619 oldenv = setzone (zone, SCM_ARG2, FUNC_NAME);
b8a1b29b
GH
620 }
621#endif
622
38c1d3c4 623#ifdef LOCALTIME_CACHE
b8a1b29b
GH
624 tzset ();
625#endif
626
a15e6dcc
MV
627 /* POSIX says strftime returns 0 on buffer overrun, but old
628 systems (i.e. libc 4 on GNU/Linux) might return `size' in that
629 case. */
630 while ((len = strftime (tbuf, size, myfmt, &t)) == 0 || len == size)
b8a1b29b 631 {
4c9419ac 632 free (tbuf);
b8a1b29b 633 size *= 2;
4c9419ac 634 tbuf = scm_malloc (size);
b8a1b29b
GH
635 }
636
637#if !defined (HAVE_TM_ZONE)
e652b54f 638 if (have_zone)
b8a1b29b
GH
639 {
640 restorezone (velts[10], oldenv, FUNC_NAME);
641 SCM_ALLOW_INTS;
642 }
38c1d3c4 643#endif
b9525b92 644 }
b8a1b29b 645
36284627 646 result = scm_mem2string (tbuf + 1, len - 1);
4c9419ac
MV
647 free (tbuf);
648 free (myfmt);
ef9ff3fd 649 return result;
b9525b92 650}
1bbd0b84 651#undef FUNC_NAME
b9525b92 652
f25f761d 653#ifdef HAVE_STRPTIME
a1ec6916 654SCM_DEFINE (scm_strptime, "strptime", 2, 0, 0,
1bbd0b84 655 (SCM format, SCM string),
a8eac221
MG
656 "Performs the reverse action to @code{strftime}, parsing\n"
657 "@var{string} according to the specification supplied in\n"
658 "@var{template}. The interpretation of month and day names is\n"
659 "dependent on the current locale. The value returned is a pair.\n"
660 "The car has an object with time components\n"
a3c8b9fc
MD
661 "in the form returned by @code{localtime} or @code{gmtime},\n"
662 "but the time zone components\n"
663 "are not usefully set.\n"
a8eac221
MG
664 "The cdr reports the number of characters from @var{string}\n"
665 "which were used for the conversion.")
1bbd0b84 666#define FUNC_NAME s_scm_strptime
b9525b92 667{
b9525b92
GH
668 struct tm t;
669 char *fmt, *str, *rest;
670
a6d9e5ab
DH
671 SCM_VALIDATE_STRING (1, format);
672 SCM_VALIDATE_STRING (2, string);
b9525b92 673
a6d9e5ab
DH
674 fmt = SCM_STRING_CHARS (format);
675 str = SCM_STRING_CHARS (string);
b9525b92
GH
676
677 /* initialize the struct tm */
678#define tm_init(field) t.field = 0
679 tm_init (tm_sec);
680 tm_init (tm_min);
681 tm_init (tm_hour);
682 tm_init (tm_mday);
683 tm_init (tm_mon);
684 tm_init (tm_year);
685 tm_init (tm_wday);
686 tm_init (tm_yday);
687#undef tm_init
688
03b79aa3
KR
689 /* GNU glibc strptime() "%s" is affected by the current timezone, since it
690 reads a UTC time_t value and converts with localtime_r() to set the tm
691 fields, hence the use of SCM_DEFER_INTS. */
b9525b92
GH
692 t.tm_isdst = -1;
693 SCM_DEFER_INTS;
694 if ((rest = strptime (str, fmt, &t)) == NULL)
73ae3b4c
KR
695 {
696 /* POSIX doesn't say strptime sets errno, and on glibc 2.3.2 for
697 instance it doesn't. Force a sensible value for our error
698 message. */
699 errno = EINVAL;
700 SCM_SYSERROR;
701 }
b9525b92
GH
702
703 SCM_ALLOW_INTS;
704 return scm_cons (filltime (&t, 0, NULL), SCM_MAKINUM (rest - str));
b9525b92 705}
1bbd0b84 706#undef FUNC_NAME
f25f761d 707#endif /* HAVE_STRPTIME */
b9525b92 708
0f2d19dd
JB
709void
710scm_init_stime()
0f2d19dd 711{
86d31dfe 712 scm_c_define ("internal-time-units-per-second",
756414cf 713 scm_long2num((long) SCM_TIME_UNITS_PER_SECOND));
0f2d19dd
JB
714
715#ifdef HAVE_FTIME
716 if (!scm_your_base.time) ftime(&scm_your_base);
717#else
718 if (!scm_your_base) time(&scm_your_base);
719#endif
720
721 if (!scm_my_base) scm_my_base = mytime();
722
876c87ce 723 scm_add_feature ("current-time");
a0599745 724#include "libguile/stime.x"
0f2d19dd
JB
725}
726
89e00824
ML
727
728/*
729 Local Variables:
730 c-file-style: "gnu"
731 End:
732*/