timemodule.c revision 6e8583dcb3c2da749349cc14621748c3920f07b2
1/***********************************************************
2Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam,
3The Netherlands.
4
5                        All Rights Reserved
6
7Permission to use, copy, modify, and distribute this software and its
8documentation for any purpose and without fee is hereby granted,
9provided that the above copyright notice appear in all copies and that
10both that copyright notice and this permission notice appear in
11supporting documentation, and that the names of Stichting Mathematisch
12Centrum or CWI not be used in advertising or publicity pertaining to
13distribution of the software without specific, written prior permission.
14
15STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
16THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
18FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
23******************************************************************/
24
25/* Time module */
26
27#include "allobjects.h"
28#include "modsupport.h"
29#include "ceval.h"
30
31#ifdef HAVE_SELECT
32#include "mymath.h"
33#endif
34
35#ifdef macintosh
36#include <time.h>
37#else
38#include <sys/types.h>
39#endif
40
41#ifdef QUICKWIN
42#include <io.h>
43#endif
44
45#ifdef HAVE_UNISTD_H
46#include <unistd.h>
47#endif
48
49#ifdef HAVE_SELECT
50#include "myselect.h"
51#else
52#include "mytime.h"
53#endif
54
55#ifdef HAVE_FTIME
56#include <sys/timeb.h>
57#endif
58
59#ifdef __WATCOMC__
60#include <i86.h>
61#else
62#ifdef MS_WINDOWS
63#include <windows.h>
64#ifdef MS_WIN16
65/* These overrides not needed for Win32 */
66#define timezone _timezone
67#define tzname _tzname
68#define daylight _daylight
69#define altzone _altzone
70#endif /* MS_WIN16 */
71#endif /* MS_WINDOWS */
72#endif /* !__WATCOMC__ */
73
74/* Forward declarations */
75static int floatsleep PROTO((double));
76static double floattime PROTO(());
77
78static object *
79time_time(self, args)
80	object *self;
81	object *args;
82{
83	double secs;
84	if (!getnoarg(args))
85		return NULL;
86	secs = floattime();
87	if (secs == 0.0) {
88		err_errno(IOError);
89		return NULL;
90	}
91	return newfloatobject(secs);
92}
93
94#ifdef HAVE_CLOCK
95
96#ifndef CLOCKS_PER_SEC
97#ifdef CLK_TCK
98#define CLOCKS_PER_SEC CLK_TCK
99#else
100#define CLOCKS_PER_SEC 1000000
101#endif
102#endif
103
104static object *
105time_clock(self, args)
106	object *self;
107	object *args;
108{
109	if (!getnoarg(args))
110		return NULL;
111	return newfloatobject(((double)clock()) / CLOCKS_PER_SEC);
112}
113#endif /* HAVE_CLOCK */
114
115static object *
116time_sleep(self, args)
117	object *self;
118	object *args;
119{
120	double secs;
121	if (!getargs(args, "d", &secs))
122		return NULL;
123	BGN_SAVE
124	if (floatsleep(secs) != 0) {
125		RET_SAVE
126		return NULL;
127	}
128	END_SAVE
129	INCREF(None);
130	return None;
131}
132
133static object *
134time_convert(when, function)
135	time_t when;
136	struct tm * (*function) PROTO((const time_t *));
137{
138	struct tm *p;
139	errno = 0;
140	p = function(&when);
141	if (p == NULL) {
142#ifdef EINVAL
143		if (errno == NULL)
144			errno = EINVAL;
145#endif
146		return err_errno(IOError);
147	}
148	return mkvalue("(iiiiiiiii)",
149		       p->tm_year + 1900,
150		       p->tm_mon + 1, /* Want January == 1 */
151		       p->tm_mday,
152		       p->tm_hour,
153		       p->tm_min,
154		       p->tm_sec,
155		       (p->tm_wday + 6) % 7, /* Want Monday == 0 */
156		       p->tm_yday + 1, /* Want January, 1 == 1 */
157		       p->tm_isdst);
158}
159
160static object *
161time_gmtime(self, args)
162	object *self;
163	object *args;
164{
165	double when;
166	if (!getargs(args, "d", &when))
167		return NULL;
168	return time_convert((time_t)when, gmtime);
169}
170
171static object *
172time_localtime(self, args)
173	object *self;
174	object *args;
175{
176	double when;
177	if (!getargs(args, "d", &when))
178		return NULL;
179	return time_convert((time_t)when, localtime);
180}
181
182static int
183gettmarg(args, p)
184	object *args;
185	struct tm *p;
186{
187	if (!getargs(args, "(iiiiiiiii)",
188		     &p->tm_year,
189		     &p->tm_mon,
190		     &p->tm_mday,
191		     &p->tm_hour,
192		     &p->tm_min,
193		     &p->tm_sec,
194		     &p->tm_wday,
195		     &p->tm_yday,
196		     &p->tm_isdst))
197		return 0;
198	if (p->tm_year >= 1900)
199		p->tm_year -= 1900;
200	p->tm_mon--;
201	p->tm_wday = (p->tm_wday + 1) % 7;
202	p->tm_yday--;
203	return 1;
204}
205
206#ifdef HAVE_STRFTIME
207static object *
208time_strftime(self, args)
209	object *self;
210	object *args;
211{
212	struct tm buf;
213	const char *fmt;
214	char *outbuf = 0;
215	int i;
216
217	if (!PyArg_ParseTuple(args, "s(iiiiiiiii)",
218			      &fmt,
219			      &(buf.tm_year),
220			      &(buf.tm_mon),
221			      &(buf.tm_mday),
222			      &(buf.tm_hour),
223			      &(buf.tm_min),
224			      &(buf.tm_sec),
225			      &(buf.tm_wday),
226			      &(buf.tm_yday),
227			      &(buf.tm_isdst)))
228		return NULL;
229	if (buf.tm_year >= 1900)
230		buf.tm_year -= 1900;
231	buf.tm_mon--;
232	buf.tm_wday = (buf.tm_wday + 1) % 7;
233	buf.tm_yday--;
234	/* I hate these functions that presume you know how big the output */
235	/* will be ahead of time... */
236	for (i = 1024 ; i < 8192 ; i += 1024) {
237		outbuf = malloc(i);
238		if (outbuf == NULL) {
239			return err_nomem();
240		}
241		if (strftime(outbuf, i-1, fmt, &buf) != 0) {
242			object *ret;
243			ret = newstringobject(outbuf);
244			free(outbuf);
245			return ret;
246		}
247		free(outbuf);
248	}
249	return err_nomem();
250}
251#endif /* HAVE_STRFTIME */
252
253static object *
254time_asctime(self, args)
255	object *self;
256	object *args;
257{
258	struct tm buf;
259	char *p;
260	if (!gettmarg(args, &buf))
261		return NULL;
262	p = asctime(&buf);
263	if (p[24] == '\n')
264		p[24] = '\0';
265	return newstringobject(p);
266}
267
268static object *
269time_ctime(self, args)
270	object *self;
271	object *args;
272{
273	double dt;
274	time_t tt;
275	char *p;
276	if (!getargs(args, "d", &dt))
277		return NULL;
278	tt = (time_t)dt;
279	p = ctime(&tt);
280	if (p[24] == '\n')
281		p[24] = '\0';
282	return newstringobject(p);
283}
284
285static object *
286time_mktime(self, args)
287	object *self;
288	object *args;
289{
290	struct tm buf;
291	time_t tt;
292	tt = time(&tt);
293	buf = *localtime(&tt);
294	if (!gettmarg(args, &buf))
295		return NULL;
296	tt = mktime(&buf);
297	if (tt == (time_t)(-1)) {
298		err_setstr(OverflowError, "mktime argument out of range");
299		return NULL;
300	}
301	return newfloatobject((double)tt);
302}
303
304static struct methodlist time_methods[] = {
305	{"time",	time_time},
306#ifdef HAVE_CLOCK
307	{"clock",	time_clock},
308#endif
309	{"sleep",	time_sleep},
310	{"gmtime",	time_gmtime},
311	{"localtime",	time_localtime},
312	{"asctime",	time_asctime},
313	{"ctime",	time_ctime},
314	{"mktime",	time_mktime},
315#ifdef HAVE_STRFTIME
316	{"strftime",	time_strftime, 1},
317#endif
318	{NULL,		NULL}		/* sentinel */
319};
320
321static void
322ins(d, name, v)
323	object *d;
324	char *name;
325	object *v;
326{
327	if (v == NULL)
328		fatal("Can't initialize time module -- NULL value");
329	if (dictinsert(d, name, v) != 0)
330		fatal("Can't initialize time module -- dictinsert failed");
331	DECREF(v);
332}
333
334void
335inittime()
336{
337	object *m, *d;
338	m = initmodule("time", time_methods);
339	d = getmoduledict(m);
340#ifdef HAVE_TZNAME
341	tzset();
342	ins(d, "timezone", newintobject((long)timezone));
343#ifdef HAVE_ALTZONE
344	ins(d, "altzone", newintobject((long)altzone));
345#else
346	ins(d, "altzone", newintobject((long)timezone-3600));
347#endif
348	ins(d, "daylight", newintobject((long)daylight));
349	ins(d, "tzname", mkvalue("(zz)", tzname[0], tzname[1]));
350#else /* !HAVE_TZNAME */
351#if HAVE_TM_ZONE
352	{
353#define YEAR ((time_t)((365 * 24 + 6) * 3600))
354		time_t t;
355		struct tm *p;
356		long winterzone, summerzone;
357		char wintername[10], summername[10];
358		/* XXX This won't work on the southern hemisphere.
359		   XXX Anybody got a better idea? */
360		t = (time((time_t *)0) / YEAR) * YEAR;
361		p = localtime(&t);
362		winterzone = -p->tm_gmtoff;
363		strncpy(wintername, p->tm_zone ? p->tm_zone : "   ", 9);
364		wintername[9] = '\0';
365		t += YEAR/2;
366		p = localtime(&t);
367		summerzone = -p->tm_gmtoff;
368		strncpy(summername, p->tm_zone ? p->tm_zone : "   ", 9);
369		summername[9] = '\0';
370		ins(d, "timezone", newintobject(winterzone));
371		ins(d, "altzone", newintobject(summerzone));
372		ins(d, "daylight", newintobject((long)(winterzone != summerzone)));
373		ins(d, "tzname",  mkvalue("(zz)", wintername, summername));
374	}
375#endif /* HAVE_TM_ZONE */
376#endif /* !HAVE_TZNAME */
377}
378
379
380/* Implement floattime() for various platforms */
381
382static double
383floattime()
384{
385	/* There are three ways to get the time:
386	   (1) gettimeofday() -- resolution in microseconds
387	   (2) ftime() -- resolution in milliseconds
388	   (3) time() -- resolution in seconds
389	   In all cases the return value is a float in seconds.
390	   Since on some systems (e.g. SCO ODT 3.0) gettimeofday() may
391	   fail, so we fall back on ftime() or time().
392	   Note: clock resolution does not imply clock accuracy! */
393#ifdef HAVE_GETTIMEOFDAY
394    {
395	struct timeval t;
396#ifdef GETTIMEOFDAY_NO_TZ
397	if (gettimeofday(&t) == 0)
398		return (double)t.tv_sec + t.tv_usec*0.000001;
399#else /* !GETTIMEOFDAY_NO_TZ */
400	if (gettimeofday(&t, (struct timezone *)NULL) == 0)
401		return (double)t.tv_sec + t.tv_usec*0.000001;
402#endif /* !GETTIMEOFDAY_NO_TZ */
403    }
404#endif /* !HAVE_GETTIMEOFDAY */
405    {
406#ifdef HAVE_FTIME
407	struct timeb t;
408	ftime(&t);
409	return (double)t.time + (double)t.millitm * (double)0.001;
410#else /* !HAVE_FTIME */
411	time_t secs;
412	time(&secs);
413	return (double)secs;
414#endif /* !HAVE_FTIME */
415    }
416}
417
418
419/* Implement floatsleep() for various platforms.
420   When interrupted (or when another error occurs), return -1 and
421   set an exception; else return 0. */
422
423static int
424#ifdef MPW
425floatsleep(double secs)
426#else
427floatsleep(secs)
428	double secs;
429#endif /* MPW */
430{
431#ifdef HAVE_SELECT
432	struct timeval t;
433	double frac;
434	frac = fmod(secs, 1.0);
435	secs = floor(secs);
436	t.tv_sec = (long)secs;
437	t.tv_usec = (long)(frac*1000000.0);
438	if (select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t) != 0) {
439		err_errno(IOError);
440		return -1;
441	}
442#else /* !HAVE_SELECT */
443#ifdef macintosh
444#define MacTicks	(* (long *)0x16A)
445	long deadline;
446	deadline = MacTicks + (long)(secs * 60.0);
447	while (MacTicks < deadline) {
448		if (sigcheck())
449			return -1;
450	}
451#else /* !macintosh */
452#ifdef __WATCOMC__
453	/* XXX Can't interrupt this sleep */
454	delay((int)(secs * 1000 + 0.5));  /* delay() uses milliseconds */
455#else /* !__WATCOMC__ */
456#ifdef MSDOS
457	struct timeb t1, t2;
458	double frac;
459	extern double fmod PROTO((double, double));
460	extern double floor PROTO((double));
461	if (secs <= 0.0)
462		return;
463	frac = fmod(secs, 1.0);
464	secs = floor(secs);
465	ftime(&t1);
466	t2.time = t1.time + (int)secs;
467	t2.millitm = t1.millitm + (int)(frac*1000.0);
468	while (t2.millitm >= 1000) {
469		t2.time++;
470		t2.millitm -= 1000;
471	}
472	for (;;) {
473#ifdef QUICKWIN
474		_wyield();
475#endif
476		if (sigcheck())
477			return -1;
478		ftime(&t1);
479		if (t1.time > t2.time ||
480		    t1.time == t2.time && t1.millitm >= t2.millitm)
481			break;
482	}
483#else /* !MSDOS */
484#ifdef MS_WIN32
485	/* XXX Can't interrupt this sleep */
486	Sleep((int)(secs*1000));
487#else /* !MS_WIN32 */
488	/* XXX Can't interrupt this sleep */
489	sleep((int)secs);
490#endif /* !MS_WIN32 */
491#endif /* !MSDOS */
492#endif /* !__WATCOMC__ */
493#endif /* !macintosh */
494#endif /* !HAVE_SELECT */
495	return 0;
496}
497