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