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