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