Merge remote-tracking branch 'origin/stable-2.0'
[bpt/guile.git] / libguile / stime.c
CommitLineData
3baf6e8b 1/* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2013 Free Software Foundation, Inc.
1c299a6b 2 *
73be1d9e 3 * This library is free software; you can redistribute it and/or
53befeb7
NJ
4 * modify it under the terms of the GNU Lesser General Public License
5 * as published by the Free Software Foundation; either version 3 of
6 * the License, or (at your option) any later version.
1c299a6b 7 *
53befeb7
NJ
8 * This library is distributed in the hope that it will be useful, but
9 * 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
53befeb7
NJ
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301 USA
73be1d9e 17 */
1bbd0b84 18
1bbd0b84 19
0f2d19dd
JB
20\f
21
464ee095
KR
22/* _POSIX_C_SOURCE is not defined always, because it causes problems on some
23 systems, notably
24
25 - FreeBSD loses all BSD and XOPEN defines.
26 - glibc loses some things like CLK_TCK.
27 - On MINGW it conflicts with the pthread headers.
28
29 But on HP-UX _POSIX_C_SOURCE is needed, as noted, for gmtime_r.
30
31 Perhaps a configure test could figure out what _POSIX_C_SOURCE gives and
32 what it takes away, and decide from that whether to use it, instead of
33 hard coding __hpux. */
34
2baec6a9
LC
35#ifndef _REENTRANT
36# define _REENTRANT /* ask solaris for gmtime_r prototype */
37#endif
464ee095 38#ifdef __hpux
95f31774 39#define _POSIX_C_SOURCE 199506L /* for gmtime_r prototype */
edea856c 40#endif
3c61e80f 41
dbb605f5 42#ifdef HAVE_CONFIG_H
3cf2f52d
RB
43# include <config.h>
44#endif
45
0f2d19dd 46#include <stdio.h>
e6e2e95a 47#include <errno.h>
69f23174 48#include <strftime.h>
587a3355 49#include <unistr.h>
e6e2e95a 50
a0599745 51#include "libguile/_scm.h"
4e047c3e 52#include "libguile/async.h"
a0599745
MD
53#include "libguile/feature.h"
54#include "libguile/strings.h"
55#include "libguile/vectors.h"
cc95e00a 56#include "libguile/dynwind.h"
587a3355 57#include "libguile/strings.h"
20e6290e 58
a0599745
MD
59#include "libguile/validate.h"
60#include "libguile/stime.h"
20e6290e 61
0f2d19dd
JB
62#ifdef HAVE_UNISTD_H
63#include <unistd.h>
64#endif
65
66\f
4a42658f
AW
67#ifdef HAVE_CLOCK_GETTIME
68# include <time.h>
69#endif
70
71#ifdef HAVE_SYS_TYPES_H
72# include <sys/types.h>
73#endif
0f2d19dd 74
a15e6dcc
MV
75#ifdef HAVE_STRING_H
76#include <string.h>
77#endif
78
b1978258
GH
79#ifdef HAVE_SYS_TIMES_H
80# include <sys/times.h>
81#endif
82
83#ifdef HAVE_SYS_TIMEB_H
84# include <sys/timeb.h>
85#endif
0f2d19dd 86
3c61e80f 87#if ! HAVE_DECL_STRPTIME
4d3bacdd
JB
88extern char *strptime ();
89#endif
90
0f2d19dd
JB
91#ifdef __STDC__
92# define timet time_t
93#else
94# define timet long
95#endif
96
eb7e1603 97
4a42658f
AW
98#if SCM_SIZEOF_LONG >= 8 && defined HAVE_CLOCK_GETTIME
99/* Nanoseconds on 64-bit systems with POSIX timers. */
100#define TIME_UNITS_PER_SECOND 1000000000
101#else
102/* Milliseconds for everyone else. */
103#define TIME_UNITS_PER_SECOND 1000
104#endif
105
106long scm_c_time_units_per_second = TIME_UNITS_PER_SECOND;
107
108static long
109time_from_seconds_and_nanoseconds (long s, long ns)
110{
111 return s * TIME_UNITS_PER_SECOND
112 + ns / (1000000000 / TIME_UNITS_PER_SECOND);
113}
114
115
116/* A runtime-selectable mechanism to choose a timing mechanism. Really
117 we want to use POSIX timers, but that's not always possible. Notably,
118 the user may have everything she needs at compile-time, but if she's
119 running on an SMP machine without a common clock source, she can't
120 use POSIX CPUTIME clocks. */
121static long (*get_internal_real_time) (void);
122static long (*get_internal_run_time) (void);
123
124
125#ifdef HAVE_CLOCK_GETTIME
126struct timespec posix_real_time_base;
127
128static long
129get_internal_real_time_posix_timer (void)
130{
131 struct timespec ts;
132 clock_gettime (CLOCK_REALTIME, &ts);
133 return time_from_seconds_and_nanoseconds
134 (ts.tv_sec - posix_real_time_base.tv_sec,
135 ts.tv_nsec - posix_real_time_base.tv_nsec);
136}
137
a02a6067
AW
138#if defined _POSIX_CPUTIME && defined CLOCK_PROCESS_CPUTIME_ID
139/* You see, FreeBSD defines _POSIX_CPUTIME but not
140 CLOCK_PROCESS_CPUTIME_ID. */
141#define HAVE_POSIX_CPUTIME 1
142
4a42658f
AW
143struct timespec posix_run_time_base;
144
145static long
146get_internal_run_time_posix_timer (void)
147{
148 struct timespec ts;
149 clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &ts);
150 return time_from_seconds_and_nanoseconds
151 (ts.tv_sec - posix_run_time_base.tv_sec,
152 ts.tv_nsec - posix_run_time_base.tv_nsec);
153}
154#endif /* _POSIX_CPUTIME */
155#endif /* HAVE_CLOCKTIME */
156
157
158#ifdef HAVE_GETTIMEOFDAY
159struct timeval gettimeofday_real_time_base;
160
161static long
162get_internal_real_time_gettimeofday (void)
163{
164 struct timeval tv;
165 gettimeofday (&tv, NULL);
166 return time_from_seconds_and_nanoseconds
167 (tv.tv_sec - gettimeofday_real_time_base.tv_sec,
168 (tv.tv_usec - gettimeofday_real_time_base.tv_usec) * 1000);
169}
170#endif
171
172
173#if defined HAVE_TIMES
174static long ticks_per_second;
175
176static long
177get_internal_run_time_times (void)
0f2d19dd
JB
178{
179 struct tms time_buffer;
180 times(&time_buffer);
4a42658f
AW
181 return (time_buffer.tms_utime + time_buffer.tms_stime)
182 * TIME_UNITS_PER_SECOND / ticks_per_second;
0f2d19dd 183}
0f2d19dd
JB
184#endif
185
4a42658f
AW
186static timet fallback_real_time_base;
187static long
188get_internal_real_time_fallback (void)
189{
190 return time_from_seconds_and_nanoseconds
191 ((long) time (NULL) - fallback_real_time_base, 0);
192}
193
1bbd0b84 194
1c299a6b 195SCM_DEFINE (scm_get_internal_real_time, "get-internal-real-time", 0, 0, 0,
1bbd0b84 196 (),
1e6808ea
MG
197 "Return the number of time units since the interpreter was\n"
198 "started.")
1bbd0b84 199#define FUNC_NAME s_scm_get_internal_real_time
0f2d19dd 200{
4a42658f 201 return scm_from_long (get_internal_real_time ());
0f2d19dd 202}
1bbd0b84
GB
203#undef FUNC_NAME
204
0f2d19dd 205
f25f761d 206#ifdef HAVE_TIMES
1c299a6b 207SCM_DEFINE (scm_times, "times", 0, 0, 0,
1bbd0b84 208 (void),
1e6808ea
MG
209 "Return an object with information about real and processor\n"
210 "time. The following procedures accept such an object as an\n"
211 "argument and return a selected component:\n"
212 "\n"
b380b885
MD
213 "@table @code\n"
214 "@item tms:clock\n"
215 "The current real time, expressed as time units relative to an\n"
216 "arbitrary base.\n"
217 "@item tms:utime\n"
218 "The CPU time units used by the calling process.\n"
219 "@item tms:stime\n"
1e6808ea
MG
220 "The CPU time units used by the system on behalf of the calling\n"
221 "process.\n"
b380b885 222 "@item tms:cutime\n"
1e6808ea
MG
223 "The CPU time units used by terminated child processes of the\n"
224 "calling process, whose status has been collected (e.g., using\n"
225 "@code{waitpid}).\n"
b380b885 226 "@item tms:cstime\n"
1e6808ea 227 "Similarly, the CPU times units used by the system on behalf of\n"
b380b885
MD
228 "terminated child processes.\n"
229 "@end table")
1bbd0b84 230#define FUNC_NAME s_scm_times
6afcd3b2 231{
6afcd3b2
GH
232 struct tms t;
233 clock_t rv;
4a42658f 234 SCM factor;
6afcd3b2 235
00ffa0e7 236 SCM result = scm_c_make_vector (5, SCM_UNDEFINED);
6afcd3b2
GH
237 rv = times (&t);
238 if (rv == -1)
1bbd0b84 239 SCM_SYSERROR;
4a42658f
AW
240
241 factor = scm_quotient (scm_from_long (TIME_UNITS_PER_SECOND),
242 scm_from_long (ticks_per_second));
243
244 SCM_SIMPLE_VECTOR_SET (result, 0,
245 scm_product (scm_from_long (rv), factor));
246 SCM_SIMPLE_VECTOR_SET (result, 1,
247 scm_product (scm_from_long (t.tms_utime), factor));
248 SCM_SIMPLE_VECTOR_SET (result, 2,
249 scm_product (scm_from_long (t.tms_stime), factor));
250 SCM_SIMPLE_VECTOR_SET (result ,3,
251 scm_product (scm_from_long (t.tms_cutime), factor));
252 SCM_SIMPLE_VECTOR_SET (result, 4,
253 scm_product (scm_from_long (t.tms_cstime), factor));
6afcd3b2 254 return result;
6afcd3b2 255}
1bbd0b84 256#undef FUNC_NAME
f25f761d 257#endif /* HAVE_TIMES */
6afcd3b2 258
1c299a6b 259long
4a42658f 260scm_c_get_internal_run_time (void)
1c299a6b 261{
4a42658f 262 return get_internal_run_time ();
1c299a6b
ML
263}
264
265SCM_DEFINE (scm_get_internal_run_time, "get-internal-run-time", 0, 0, 0,
1bbd0b84 266 (void),
1e6808ea
MG
267 "Return the number of time units of processor time used by the\n"
268 "interpreter. Both @emph{system} and @emph{user} time are\n"
269 "included but subprocesses are not.")
1bbd0b84 270#define FUNC_NAME s_scm_get_internal_run_time
0f2d19dd 271{
b9bd8526 272 return scm_from_long (scm_c_get_internal_run_time ());
0f2d19dd 273}
1bbd0b84 274#undef FUNC_NAME
0f2d19dd 275
f40771d8
KR
276/* For reference, note that current-time and gettimeofday both should be
277 protected against setzone/restorezone changes in another thread, since on
278 DOS the system time is normally kept as local time, which means TZ
279 affects the return from current-time and gettimeofday. Not sure if DJGPP
280 etc actually has concurrent multi-threading, but it seems prudent not to
281 make assumptions about this. */
282
1c299a6b 283SCM_DEFINE (scm_current_time, "current-time", 0, 0, 0,
1bbd0b84 284 (void),
1e6808ea
MG
285 "Return the number of seconds since 1970-01-01 00:00:00 UTC,\n"
286 "excluding leap seconds.")
1bbd0b84 287#define FUNC_NAME s_scm_current_time
0f2d19dd 288{
19468eff
GH
289 timet timv;
290
9de87eea 291 SCM_CRITICAL_SECTION_START;
763313a2 292 timv = time (NULL);
9de87eea 293 SCM_CRITICAL_SECTION_END;
763313a2
KR
294 if (timv == -1)
295 SCM_MISC_ERROR ("current time not available", SCM_EOL);
b9bd8526 296 return scm_from_long (timv);
19468eff 297}
1bbd0b84 298#undef FUNC_NAME
19468eff 299
1c299a6b 300SCM_DEFINE (scm_gettimeofday, "gettimeofday", 0, 0, 0,
1bbd0b84 301 (void),
1e6808ea
MG
302 "Return a pair containing the number of seconds and microseconds\n"
303 "since 1970-01-01 00:00:00 UTC, excluding leap seconds. Note:\n"
304 "whether true microsecond resolution is available depends on the\n"
305 "operating system.")
1bbd0b84 306#define FUNC_NAME s_scm_gettimeofday
19468eff
GH
307{
308#ifdef HAVE_GETTIMEOFDAY
309 struct timeval time;
310
4a42658f
AW
311 if (gettimeofday (&time, NULL))
312 SCM_SYSERROR;
313
b9bd8526
MV
314 return scm_cons (scm_from_long (time.tv_sec),
315 scm_from_long (time.tv_usec));
19468eff 316#else
4a42658f
AW
317 timet t = time (NULL);
318 if (errno)
319 SCM_SYSERROR;
320 else
321 return scm_cons (scm_from_long ((long)t), SCM_INUM0);
19468eff
GH
322#endif
323}
1bbd0b84 324#undef FUNC_NAME
19468eff
GH
325
326static SCM
d6d9e957 327filltime (struct tm *bd_time, int zoff, const char *zname)
19468eff 328{
00ffa0e7 329 SCM result = scm_c_make_vector (11, SCM_UNDEFINED);
19468eff 330
4057a3e0
MV
331 SCM_SIMPLE_VECTOR_SET (result,0, scm_from_int (bd_time->tm_sec));
332 SCM_SIMPLE_VECTOR_SET (result,1, scm_from_int (bd_time->tm_min));
333 SCM_SIMPLE_VECTOR_SET (result,2, scm_from_int (bd_time->tm_hour));
334 SCM_SIMPLE_VECTOR_SET (result,3, scm_from_int (bd_time->tm_mday));
335 SCM_SIMPLE_VECTOR_SET (result,4, scm_from_int (bd_time->tm_mon));
336 SCM_SIMPLE_VECTOR_SET (result,5, scm_from_int (bd_time->tm_year));
337 SCM_SIMPLE_VECTOR_SET (result,6, scm_from_int (bd_time->tm_wday));
338 SCM_SIMPLE_VECTOR_SET (result,7, scm_from_int (bd_time->tm_yday));
339 SCM_SIMPLE_VECTOR_SET (result,8, scm_from_int (bd_time->tm_isdst));
340 SCM_SIMPLE_VECTOR_SET (result,9, scm_from_int (zoff));
341 SCM_SIMPLE_VECTOR_SET (result,10, (zname
342 ? scm_from_locale_string (zname)
343 : SCM_BOOL_F));
19468eff
GH
344 return result;
345}
346
ef9ff3fd 347static char tzvar[3] = "TZ";
ef9ff3fd 348
38c1d3c4
GH
349/* if zone is set, create a temporary environment with only a TZ
350 string. other threads or interrupt handlers shouldn't be allowed
351 to run until the corresponding restorezone is called. hence the use
352 of a static variable for tmpenv is no big deal. */
ef9ff3fd 353static char **
3eeba8d4 354setzone (SCM zone, int pos, const char *subr)
b9525b92 355{
ef9ff3fd 356 char **oldenv = 0;
b9525b92
GH
357
358 if (!SCM_UNBNDP (zone))
359 {
ef9ff3fd 360 static char *tmpenv[2];
b9525b92 361 char *buf;
7f9994d9
MV
362 size_t zone_len;
363
364 zone_len = scm_to_locale_stringbuf (zone, NULL, 0);
365 buf = scm_malloc (zone_len + sizeof (tzvar) + 1);
366 strcpy (buf, tzvar);
367 buf[sizeof(tzvar)-1] = '=';
368 scm_to_locale_stringbuf (zone, buf+sizeof(tzvar), zone_len);
369 buf[sizeof(tzvar)+zone_len] = '\0';
ef9ff3fd
GH
370 oldenv = environ;
371 tmpenv[0] = buf;
372 tmpenv[1] = 0;
373 environ = tmpenv;
b9525b92 374 }
ef9ff3fd 375 return oldenv;
b9525b92
GH
376}
377
378static void
e81d98ec 379restorezone (SCM zone, char **oldenv, const char *subr SCM_UNUSED)
b9525b92
GH
380{
381 if (!SCM_UNBNDP (zone))
382 {
4c9419ac 383 free (environ[0]);
ef9ff3fd 384 environ = oldenv;
38c1d3c4
GH
385#ifdef HAVE_TZSET
386 /* for the possible benefit of user code linked with libguile. */
b9525b92 387 tzset();
38c1d3c4 388#endif
b9525b92
GH
389 }
390}
391
1c299a6b 392SCM_DEFINE (scm_localtime, "localtime", 1, 1, 0,
1bbd0b84 393 (SCM time, SCM zone),
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 time zone for the calculation is\n"
397 "optionally specified by @var{zone} (a string), otherwise the\n"
398 "@code{TZ} environment variable or the system default is used.")
1bbd0b84 399#define FUNC_NAME s_scm_localtime
19468eff
GH
400{
401 timet itime;
4edc089c 402 struct tm *ltptr, lt, *utc;
19468eff
GH
403 SCM result;
404 int zoff;
405 char *zname = 0;
ef9ff3fd 406 char **oldenv;
19468eff
GH
407 int err;
408
e4b265d8 409 itime = SCM_NUM2LONG (1, time);
38c1d3c4
GH
410
411 /* deferring interupts is essential since a) setzone may install a temporary
412 environment b) localtime uses a static buffer. */
9de87eea 413 SCM_CRITICAL_SECTION_START;
1bbd0b84 414 oldenv = setzone (zone, SCM_ARG2, FUNC_NAME);
38c1d3c4
GH
415#ifdef LOCALTIME_CACHE
416 tzset ();
417#endif
73ae3b4c
KR
418 /* POSIX says localtime sets errno, but C99 doesn't say that.
419 Give a sensible default value in case localtime doesn't set it. */
420 errno = EINVAL;
4edc089c 421 ltptr = localtime (&itime);
19468eff 422 err = errno;
4edc089c 423 if (ltptr)
19468eff 424 {
4d3bacdd 425 const char *ptr;
ef9ff3fd 426
43ff3170
GH
427 /* copy zone name before calling gmtime or restoring zone. */
428#if defined (HAVE_TM_ZONE)
ef9ff3fd 429 ptr = ltptr->tm_zone;
43ff3170 430#elif defined (HAVE_TZNAME)
ef9ff3fd 431 ptr = tzname[ (ltptr->tm_isdst == 1) ? 1 : 0 ];
43ff3170
GH
432#else
433 ptr = "";
b9525b92 434#endif
4c9419ac 435 zname = scm_malloc (strlen (ptr) + 1);
ef9ff3fd 436 strcpy (zname, ptr);
19468eff 437 }
ef9ff3fd
GH
438 /* the struct is copied in case localtime and gmtime share a buffer. */
439 if (ltptr)
440 lt = *ltptr;
73ae3b4c
KR
441 /* POSIX says gmtime sets errno, but C99 doesn't say that.
442 Give a sensible default value in case gmtime doesn't set it. */
443 errno = EINVAL;
ef9ff3fd
GH
444 utc = gmtime (&itime);
445 if (utc == NULL)
446 err = errno;
1bbd0b84 447 restorezone (zone, oldenv, FUNC_NAME);
b9525b92 448 /* delayed until zone has been restored. */
19468eff 449 errno = err;
4edc089c 450 if (utc == NULL || ltptr == NULL)
1bbd0b84 451 SCM_SYSERROR;
19468eff
GH
452
453 /* calculate timezone offset in seconds west of UTC. */
4edc089c
GH
454 zoff = (utc->tm_hour - lt.tm_hour) * 3600 + (utc->tm_min - lt.tm_min) * 60
455 + utc->tm_sec - lt.tm_sec;
456 if (utc->tm_year < lt.tm_year)
19468eff 457 zoff -= 24 * 60 * 60;
4edc089c 458 else if (utc->tm_year > lt.tm_year)
19468eff 459 zoff += 24 * 60 * 60;
4edc089c 460 else if (utc->tm_yday < lt.tm_yday)
19468eff 461 zoff -= 24 * 60 * 60;
4edc089c 462 else if (utc->tm_yday > lt.tm_yday)
19468eff 463 zoff += 24 * 60 * 60;
1c299a6b 464
4edc089c 465 result = filltime (&lt, zoff, zname);
9de87eea 466 SCM_CRITICAL_SECTION_END;
c291b588
LC
467
468 free (zname);
19468eff
GH
469 return result;
470}
1bbd0b84 471#undef FUNC_NAME
19468eff 472
45198ffb
KR
473/* tm_zone is normally a pointer, not an array within struct tm, so we might
474 have to worry about the lifespan of what it points to. The posix specs
475 don't seem to say anything about this, let's assume here that tm_zone
476 will be a constant and therefore no protection or anything is needed
477 until we copy it in filltime(). */
478
1c299a6b 479SCM_DEFINE (scm_gmtime, "gmtime", 1, 0, 0,
1bbd0b84 480 (SCM time),
1e6808ea
MG
481 "Return an object representing the broken down components of\n"
482 "@var{time}, an integer like the one returned by\n"
483 "@code{current-time}. The values are calculated for UTC.")
1bbd0b84 484#define FUNC_NAME s_scm_gmtime
19468eff
GH
485{
486 timet itime;
45198ffb 487 struct tm bd_buf, *bd_time;
d6d9e957 488 const char *zname;
19468eff 489
e4b265d8 490 itime = SCM_NUM2LONG (1, time);
45198ffb 491
73ae3b4c
KR
492 /* POSIX says gmtime sets errno, but C99 doesn't say that.
493 Give a sensible default value in case gmtime doesn't set it. */
494 errno = EINVAL;
45198ffb
KR
495
496#if HAVE_GMTIME_R
497 bd_time = gmtime_r (&itime, &bd_buf);
498#else
9de87eea 499 SCM_CRITICAL_SECTION_START;
19468eff 500 bd_time = gmtime (&itime);
45198ffb
KR
501 if (bd_time != NULL)
502 bd_buf = *bd_time;
9de87eea 503 SCM_CRITICAL_SECTION_END;
45198ffb 504#endif
19468eff 505 if (bd_time == NULL)
1bbd0b84 506 SCM_SYSERROR;
45198ffb 507
d6d9e957 508#if HAVE_STRUCT_TM_TM_ZONE
45198ffb 509 zname = bd_buf.tm_zone;
d6d9e957
KR
510#else
511 zname = "GMT";
512#endif
45198ffb 513 return filltime (&bd_buf, 0, zname);
19468eff 514}
1bbd0b84 515#undef FUNC_NAME
19468eff 516
b9525b92
GH
517/* copy time components from a Scheme object to a struct tm. */
518static void
3eeba8d4 519bdtime2c (SCM sbd_time, struct tm *lt, int pos, const char *subr)
19468eff 520{
4057a3e0
MV
521 SCM_ASSERT (scm_is_simple_vector (sbd_time)
522 && SCM_SIMPLE_VECTOR_LENGTH (sbd_time) == 11,
55a7fc62
GH
523 sbd_time, pos, subr);
524
4057a3e0
MV
525 lt->tm_sec = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 0));
526 lt->tm_min = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 1));
527 lt->tm_hour = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 2));
528 lt->tm_mday = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 3));
529 lt->tm_mon = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 4));
530 lt->tm_year = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 5));
531 lt->tm_wday = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 6));
532 lt->tm_yday = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 7));
533 lt->tm_isdst = scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 8));
8ab3d8a0
KR
534#if HAVE_STRUCT_TM_TM_GMTOFF
535 lt->tm_gmtoff = - scm_to_int (SCM_SIMPLE_VECTOR_REF (sbd_time, 9));
536#endif
c7abe4f3 537#ifdef HAVE_TM_ZONE
4057a3e0 538 if (scm_is_false (SCM_SIMPLE_VECTOR_REF (sbd_time, 10)))
55a7fc62
GH
539 lt->tm_zone = NULL;
540 else
4057a3e0 541 lt->tm_zone = scm_to_locale_string (SCM_SIMPLE_VECTOR_REF (sbd_time, 10));
c7abe4f3 542#endif
b9525b92
GH
543}
544
1c299a6b 545SCM_DEFINE (scm_mktime, "mktime", 1, 1, 0,
1bbd0b84 546 (SCM sbd_time, SCM zone),
b7e64f8b
BT
547 "@var{sbd_time} is an object representing broken down time and\n"
548 "@code{zone} is an optional time zone specifier (otherwise the\n"
549 "TZ environment variable or the system default is used).\n"
550 "\n"
551 "Returns a pair: the car is a corresponding integer time value\n"
552 "like that returned by @code{current-time}; the cdr is a broken\n"
553 "down time object, similar to as @var{sbd_time} but with\n"
554 "normalized values.")
1bbd0b84 555#define FUNC_NAME s_scm_mktime
b9525b92
GH
556{
557 timet itime;
558 struct tm lt, *utc;
559 SCM result;
560 int zoff;
561 char *zname = 0;
ef9ff3fd 562 char **oldenv;
b9525b92
GH
563 int err;
564
661ae7ab 565 scm_dynwind_begin (0);
cc95e00a 566
1bbd0b84 567 bdtime2c (sbd_time, &lt, SCM_ARG1, FUNC_NAME);
edea856c 568#if HAVE_STRUCT_TM_TM_ZONE
661ae7ab 569 scm_dynwind_free ((char *)lt.tm_zone);
edea856c 570#endif
19468eff 571
661ae7ab 572 scm_dynwind_critical_section (SCM_BOOL_F);
9291479f 573
1bbd0b84 574 oldenv = setzone (zone, SCM_ARG2, FUNC_NAME);
38c1d3c4
GH
575#ifdef LOCALTIME_CACHE
576 tzset ();
577#endif
19468eff 578 itime = mktime (&lt);
73ae3b4c
KR
579 /* POSIX doesn't say mktime sets errno, and on glibc 2.3.2 for instance it
580 doesn't. Force a sensible value for our error message. */
581 err = EINVAL;
19468eff 582
b9525b92
GH
583 if (itime != -1)
584 {
4d3bacdd 585 const char *ptr;
ef9ff3fd 586
43ff3170
GH
587 /* copy zone name before calling gmtime or restoring the zone. */
588#if defined (HAVE_TM_ZONE)
ef9ff3fd 589 ptr = lt.tm_zone;
43ff3170 590#elif defined (HAVE_TZNAME)
ef9ff3fd 591 ptr = tzname[ (lt.tm_isdst == 1) ? 1 : 0 ];
43ff3170
GH
592#else
593 ptr = "";
b9525b92 594#endif
4c9419ac 595 zname = scm_malloc (strlen (ptr) + 1);
ef9ff3fd 596 strcpy (zname, ptr);
b9525b92 597 }
ef9ff3fd
GH
598
599 /* get timezone offset in seconds west of UTC. */
73ae3b4c
KR
600 /* POSIX says gmtime sets errno, but C99 doesn't say that.
601 Give a sensible default value in case gmtime doesn't set it. */
fa0198bf 602 errno = EINVAL;
ef9ff3fd
GH
603 utc = gmtime (&itime);
604 if (utc == NULL)
605 err = errno;
606
1bbd0b84 607 restorezone (zone, oldenv, FUNC_NAME);
b9525b92
GH
608 /* delayed until zone has been restored. */
609 errno = err;
610 if (utc == NULL || itime == -1)
1bbd0b84 611 SCM_SYSERROR;
b9525b92 612
19468eff
GH
613 zoff = (utc->tm_hour - lt.tm_hour) * 3600 + (utc->tm_min - lt.tm_min) * 60
614 + utc->tm_sec - lt.tm_sec;
615 if (utc->tm_year < lt.tm_year)
616 zoff -= 24 * 60 * 60;
617 else if (utc->tm_year > lt.tm_year)
618 zoff += 24 * 60 * 60;
619 else if (utc->tm_yday < lt.tm_yday)
620 zoff -= 24 * 60 * 60;
621 else if (utc->tm_yday > lt.tm_yday)
622 zoff += 24 * 60 * 60;
623
b9bd8526 624 result = scm_cons (scm_from_long (itime),
19468eff 625 filltime (&lt, zoff, zname));
c291b588 626 free (zname);
cc95e00a 627
661ae7ab 628 scm_dynwind_end ();
19468eff 629 return result;
0f2d19dd 630}
1bbd0b84 631#undef FUNC_NAME
0f2d19dd 632
38c1d3c4 633#ifdef HAVE_TZSET
1c299a6b 634SCM_DEFINE (scm_tzset, "tzset", 0, 0, 0,
1bbd0b84 635 (void),
a3c8b9fc
MD
636 "Initialize the timezone from the TZ environment variable\n"
637 "or the system default. It's not usually necessary to call this procedure\n"
638 "since it's done automatically by other procedures that depend on the\n"
639 "timezone.")
1bbd0b84 640#define FUNC_NAME s_scm_tzset
0f2d19dd 641{
19468eff
GH
642 tzset();
643 return SCM_UNSPECIFIED;
0f2d19dd 644}
1bbd0b84 645#undef FUNC_NAME
38c1d3c4 646#endif /* HAVE_TZSET */
0f2d19dd 647
a1ec6916 648SCM_DEFINE (scm_strftime, "strftime", 2, 0, 0,
1bbd0b84 649 (SCM format, SCM stime),
5cd87f55
KR
650 "Return a string which is broken-down time structure @var{stime}\n"
651 "formatted according to the given @var{format} string.\n"
652 "\n"
653 "@var{format} contains field specifications introduced by a\n"
654 "@samp{%} character. See @ref{Formatting Calendar Time,,, libc,\n"
655 "The GNU C Library Reference Manual}, or @samp{man 3 strftime},\n"
656 "for the available formatting.\n"
657 "\n"
658 "@lisp\n"
659 "(strftime \"%c\" (localtime (current-time)))\n"
660 "@result{} \"Mon Mar 11 20:17:43 2002\"\n"
661 "@end lisp\n"
662 "\n"
663 "If @code{setlocale} has been called (@pxref{Locales}), month\n"
664 "and day names are from the current locale and in the locale\n"
665 "character set.")
1bbd0b84 666#define FUNC_NAME s_scm_strftime
b9525b92
GH
667{
668 struct tm t;
669
d40e1ca8 670 char *tbuf;
b9525b92 671 int size = 50;
d40e1ca8
AW
672 char *fmt;
673 char *myfmt;
674 size_t len;
ef9ff3fd 675 SCM result;
b9525b92 676
a6d9e5ab 677 SCM_VALIDATE_STRING (1, format);
1bbd0b84 678 bdtime2c (stime, &t, SCM_ARG2, FUNC_NAME);
b9525b92 679
587a3355
MG
680 /* Convert string to UTF-8 so that non-ASCII characters in the
681 format are passed through unchanged. */
d40e1ca8 682 fmt = scm_to_utf8_stringn (format, &len);
b9525b92 683
a15e6dcc
MV
684 /* Ugly hack: strftime can return 0 if its buffer is too small,
685 but some valid time strings (e.g. "%p") can sometimes produce
686 a zero-byte output string! Workaround is to prepend a junk
687 character to the format string, so that valid returns are always
688 nonzero. */
4c9419ac 689 myfmt = scm_malloc (len+2);
587a3355 690 *myfmt = (scm_t_uint8) 'x';
d40e1ca8 691 strncpy (myfmt + 1, fmt, len);
587a3355
MG
692 myfmt[len + 1] = 0;
693 scm_remember_upto_here_1 (format);
694 free (fmt);
a15e6dcc 695
4c9419ac 696 tbuf = scm_malloc (size);
b8a1b29b
GH
697 {
698#if !defined (HAVE_TM_ZONE)
699 /* it seems the only way to tell non-GNU versions of strftime what
700 zone to use (for the %Z format) is to set TZ in the
701 environment. interrupts and thread switching must be deferred
702 until TZ is restored. */
703 char **oldenv = NULL;
ecc9f40f 704 SCM zone_spec = SCM_SIMPLE_VECTOR_REF (stime, 10);
e652b54f 705 int have_zone = 0;
b8a1b29b 706
ecc9f40f 707 if (scm_is_true (zone_spec) && scm_c_string_length (zone_spec) > 0)
b8a1b29b
GH
708 {
709 /* it's not required that the TZ setting be correct, just that
e652b54f
GH
710 it has the right name. so try something like TZ=EST0.
711 using only TZ=EST would be simpler but it doesn't work on
712 some OSs, e.g., Solaris. */
713 SCM zone =
ecc9f40f
MV
714 scm_string_append (scm_list_2 (zone_spec,
715 scm_from_locale_string ("0")));
1c299a6b 716
e652b54f 717 have_zone = 1;
9de87eea 718 SCM_CRITICAL_SECTION_START;
e652b54f 719 oldenv = setzone (zone, SCM_ARG2, FUNC_NAME);
b8a1b29b
GH
720 }
721#endif
722
38c1d3c4 723#ifdef LOCALTIME_CACHE
b8a1b29b
GH
724 tzset ();
725#endif
726
69f23174
LC
727 /* Use `nstrftime ()' from Gnulib, which supports all GNU extensions
728 supported by glibc. */
d40e1ca8 729 while ((len = nstrftime (tbuf, size, myfmt, &t, 0, 0)) == 0)
b8a1b29b 730 {
4c9419ac 731 free (tbuf);
b8a1b29b 732 size *= 2;
4c9419ac 733 tbuf = scm_malloc (size);
b8a1b29b
GH
734 }
735
736#if !defined (HAVE_TM_ZONE)
e652b54f 737 if (have_zone)
b8a1b29b 738 {
ecc9f40f 739 restorezone (zone_spec, oldenv, FUNC_NAME);
9de87eea 740 SCM_CRITICAL_SECTION_END;
b8a1b29b 741 }
38c1d3c4 742#endif
b9525b92 743 }
b8a1b29b 744
d40e1ca8 745 result = scm_from_utf8_string (tbuf + 1);
4c9419ac
MV
746 free (tbuf);
747 free (myfmt);
168e958c
KR
748#if HAVE_STRUCT_TM_TM_ZONE
749 free ((char *) t.tm_zone);
750#endif
ef9ff3fd 751 return result;
b9525b92 752}
1bbd0b84 753#undef FUNC_NAME
b9525b92 754
f25f761d 755#ifdef HAVE_STRPTIME
a1ec6916 756SCM_DEFINE (scm_strptime, "strptime", 2, 0, 0,
1bbd0b84 757 (SCM format, SCM string),
a8eac221
MG
758 "Performs the reverse action to @code{strftime}, parsing\n"
759 "@var{string} according to the specification supplied in\n"
b7e64f8b 760 "@var{format}. The interpretation of month and day names is\n"
a8eac221
MG
761 "dependent on the current locale. The value returned is a pair.\n"
762 "The car has an object with time components\n"
a3c8b9fc
MD
763 "in the form returned by @code{localtime} or @code{gmtime},\n"
764 "but the time zone components\n"
765 "are not usefully set.\n"
a8eac221
MG
766 "The cdr reports the number of characters from @var{string}\n"
767 "which were used for the conversion.")
1bbd0b84 768#define FUNC_NAME s_scm_strptime
b9525b92 769{
b9525b92 770 struct tm t;
d40e1ca8 771 char *fmt, *str, *rest;
587a3355 772 size_t used_len;
8ab3d8a0 773 long zoff;
b9525b92 774
a6d9e5ab
DH
775 SCM_VALIDATE_STRING (1, format);
776 SCM_VALIDATE_STRING (2, string);
b9525b92 777
587a3355
MG
778 /* Convert strings to UTF-8 so that non-ASCII characters are passed
779 through unchanged. */
d40e1ca8
AW
780 fmt = scm_to_utf8_string (format);
781 str = scm_to_utf8_string (string);
b9525b92
GH
782
783 /* initialize the struct tm */
784#define tm_init(field) t.field = 0
785 tm_init (tm_sec);
786 tm_init (tm_min);
787 tm_init (tm_hour);
788 tm_init (tm_mday);
789 tm_init (tm_mon);
790 tm_init (tm_year);
791 tm_init (tm_wday);
792 tm_init (tm_yday);
8ab3d8a0
KR
793#if HAVE_STRUCT_TM_TM_GMTOFF
794 tm_init (tm_gmtoff);
795#endif
b9525b92
GH
796#undef tm_init
797
03b79aa3
KR
798 /* GNU glibc strptime() "%s" is affected by the current timezone, since it
799 reads a UTC time_t value and converts with localtime_r() to set the tm
9de87eea 800 fields, hence the use of SCM_CRITICAL_SECTION_START. */
b9525b92 801 t.tm_isdst = -1;
9de87eea 802 SCM_CRITICAL_SECTION_START;
d40e1ca8 803 rest = strptime (str, fmt, &t);
9de87eea 804 SCM_CRITICAL_SECTION_END;
763313a2 805 if (rest == NULL)
73ae3b4c
KR
806 {
807 /* POSIX doesn't say strptime sets errno, and on glibc 2.3.2 for
808 instance it doesn't. Force a sensible value for our error
809 message. */
810 errno = EINVAL;
587a3355
MG
811 scm_remember_upto_here_2 (format, string);
812 free (str);
813 free (fmt);
73ae3b4c
KR
814 SCM_SYSERROR;
815 }
b9525b92 816
8ab3d8a0
KR
817 /* tm_gmtoff is set by GNU glibc strptime "%s", so capture it when
818 available */
819#if HAVE_STRUCT_TM_TM_GMTOFF
820 zoff = - t.tm_gmtoff; /* seconds west, not east */
821#else
822 zoff = 0;
823#endif
824
587a3355 825 /* Compute the number of UTF-8 characters. */
d40e1ca8 826 used_len = u8_strnlen ((scm_t_uint8*) str, rest-str);
587a3355
MG
827 scm_remember_upto_here_2 (format, string);
828 free (str);
829 free (fmt);
830
8ab3d8a0 831 return scm_cons (filltime (&t, zoff, NULL),
587a3355 832 scm_from_signed_integer (used_len));
b9525b92 833}
1bbd0b84 834#undef FUNC_NAME
f25f761d 835#endif /* HAVE_STRPTIME */
b9525b92 836
0f2d19dd
JB
837void
838scm_init_stime()
0f2d19dd 839{
86d31dfe 840 scm_c_define ("internal-time-units-per-second",
b9bd8526 841 scm_from_long (SCM_TIME_UNITS_PER_SECOND));
0f2d19dd 842
4a42658f
AW
843 /* Init POSIX timers, and see if we can use them. */
844#ifdef HAVE_CLOCK_GETTIME
845 if (clock_gettime (CLOCK_REALTIME, &posix_real_time_base) == 0)
846 get_internal_real_time = get_internal_real_time_posix_timer;
847
a02a6067 848#ifdef HAVE_POSIX_CPUTIME
4a42658f
AW
849 {
850 clockid_t dummy;
851
852 /* Only use the _POSIX_CPUTIME clock if it's going to work across
853 CPUs. */
854 if (clock_getcpuclockid (0, &dummy) == 0 &&
855 clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &posix_run_time_base) == 0)
856 get_internal_run_time = get_internal_run_time_posix_timer;
857 else
858 errno = 0;
859 }
a02a6067 860#endif /* HAVE_POSIX_CPUTIME */
4a42658f
AW
861#endif /* HAVE_CLOCKTIME */
862
863 /* If needed, init and use gettimeofday timer. */
864#ifdef HAVE_GETTIMEOFDAY
865 if (!get_internal_real_time
866 && gettimeofday (&gettimeofday_real_time_base, NULL) == 0)
867 get_internal_real_time = get_internal_real_time_gettimeofday;
868#endif
869
870 /* Init ticks_per_second for scm_times, and use times(2)-based
871 run-time timer if needed. */
872#ifdef HAVE_TIMES
873#ifdef _SC_CLK_TCK
874 ticks_per_second = sysconf (_SC_CLK_TCK);
0f2d19dd 875#else
4a42658f 876 ticks_per_second = CLK_TCK;
0f2d19dd 877#endif
4a42658f
AW
878 if (!get_internal_run_time)
879 get_internal_run_time = get_internal_run_time_times;
880#endif
881
882 if (!get_internal_real_time)
883 /* No POSIX timers, gettimeofday doesn't work... badness! */
884 {
885 fallback_real_time_base = time (NULL);
886 get_internal_real_time = get_internal_real_time_fallback;
887 }
0f2d19dd 888
4a42658f
AW
889 /* If we don't have a run-time timer, use real-time. */
890 if (!get_internal_run_time)
891 get_internal_run_time = get_internal_real_time;
0f2d19dd 892
876c87ce 893 scm_add_feature ("current-time");
a0599745 894#include "libguile/stime.x"
0f2d19dd
JB
895}
896
89e00824
ML
897
898/*
899 Local Variables:
900 c-file-style: "gnu"
901 End:
902*/