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