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