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