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