/*** analog 4.13 http://www.analog.cx/ ***/ /*** This program is copyright (c) Stephen R. E. Turner 1995 - 2000 except as *** stated otherwise. Distribution, usage and modification of this program is *** subject to the conditions of the Licence which you should have received *** with it. This program comes with no warranty, expressed or implied. ***/ /*** dates.c; anything to do with dates. ***/ #include "anlghea3.h" unsigned int daysbefore[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; /* in non-leap year */ unsigned int monthlength[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; char *engmonths[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; char *engdays[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; #define FEB (1) #define DEC (11) #define IS_LEAPYEAR(y) ((y) % 4 == 0) /* This is not Year 2100 compatible but is Year 2000 compatible! */ /* Analog stores times internally in minutes since 30-31/12/1969. (Not in "Unix time" because it works better if all valid times are > 0). */ #define DATE2CODE(y, m, d) (((y) - 1970) * 365 + ((y) - 1968) / 4 + \ daysbefore[m] + (d) - (IS_LEAPYEAR(y) && (m) <= FEB)) #define TIMECODE(datecode, hr, min) ((timecode_t)((datecode) * 1440 + (datecode_t)((hr) * 60 + (min)))) #define UXTIME2CODE(t) ((timecode_t)(((t) / 60) + 1440)) void code2date(datecode_t code, unsigned int *date, unsigned int *month, unsigned int *year) { /* not most efficient possible, but only used in output so doesn't need to be super-fast */ code += 364; /* so 0 -> 1/1/69 */ *year = 1969 + 4 * (code / 1461); code %= 1461; *year += MIN(code / 365, 3); /* no leap days for a while after 1/1/69! */ if (code == 1460) { *month = DEC; *date = 31; } else { code %= 365; /* so 0 -> 1/Jan, any year */ for (*month = DEC; daysbefore[*month] + (IS_LEAPYEAR(*year) && *month > FEB) > code; (*month)--) ; /* run to right month */ *date = code - daysbefore[*month] + 1 - (IS_LEAPYEAR(*year) && *month > FEB); } } time_t shifttime(time_t timer, int diff) { /* Surprisingly, there is no ANSI C function to do this */ struct tm *lt = localtime(&timer); lt->tm_min += diff; timer = mktime(lt); return(timer); } choice parsedate(time_t starttime, char *s, timecode_t *date, logical from, logical unixtime) { struct tm *st; int y, m, d, h, n; char *p; #ifndef NOGMTIME if (unixtime) st = gmtime(&starttime); else #endif st = localtime(&starttime); if (ISDIGIT(*s) && ISDIGIT(*(s + 1))) { y = 10 * (*s - '0') + (*(s + 1) - '0'); s += 2; y += 1900; if (y < 1970) y += 100; } else if (*s == '+' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) { y = st->tm_year + 1900 + 10 * (*(s + 1) - '0') + (*(s + 2) - '0'); s += 3; } else if (*s == '-' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) { y = st->tm_year + 1900 - 10 * (*(s + 1) - '0') - (*(s + 2) - '0'); s += 3; } else return(ERR); if (ISDIGIT(*s) && ISDIGIT(*(s + 1))) { m = 10 * (*s - '0') + (*(s + 1) - '0') - 1; if (m > 11 || m < 0) return(ERR); s += 2; } else if (*s == '+' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) { m = st->tm_mon + 10 * (*(s + 1) - '0') + (*(s + 2) - '0'); s += 3; } else if (*s == '-' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) { m = st->tm_mon - 10 * (*(s + 1) - '0') - (*(s + 2) - '0'); s += 3; } else return(ERR); while (m < 0) { /* need to do this now because about to use monthlength */ m += 12; y--; } while (m > 11) { m -= 12; y++; } if (ISDIGIT(*s) && ISDIGIT(*(s + 1))) { d = (int)strtol(s, &p, 10); if (d > 31 || d <= 0) return(ERR); else if (d > (int)(monthlength[m]) + (m == FEB && IS_LEAPYEAR(y))) d = monthlength[m] + (m == FEB && IS_LEAPYEAR(y)); } /* relative dates must be >= 2 digits but can be more */ else if (*s == '+' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) d = st->tm_mday + (int)strtol(s + 1, &p, 10); else if (*s == '-' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) d = st->tm_mday - (int)strtol(s + 1, &p, 10); else return(ERR); if (*p == ':') { /* parse hr & min */ s = p + 1; if (ISDIGIT(*s) && ISDIGIT(*(s + 1))) { h = 10 * (*s - '0') + (*(s + 1) - '0'); if (h > 23) return(ERR); s += 2; } else if (*s == '+' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) { h = st->tm_hour + 10 * (*(s + 1) - '0') + (*(s + 2) - '0'); s += 3; } else if (*s == '-' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) { h = st->tm_hour - 10 * (*(s + 1) - '0') - (*(s + 2) - '0'); s += 3; } else return(ERR); if (ISDIGIT(*s) && ISDIGIT(*(s + 1))) { n = 10 * (*s - '0') + (*(s + 1) - '0'); if (n > 59) return(ERR); s += 2; } else if (*s == '+' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) { n = st->tm_min + 10 * (*(s + 1) - '0') + (*(s + 2) - '0'); s += 3; } else if (*s == '-' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) { n = st->tm_min - 10 * (*(s + 1) - '0') - (*(s + 2) - '0'); s += 3; } else return(ERR); } /* end *p == ':' */ else if (*p == '\0' || *p == 'd' || *p == 'D') { /* d can come from floor */ if (from) { h = 0; n = 0; } else { h = 23; n = 59; } } else return(ERR); while (n < 0) { n += 60; h--; } while (n > 59) { n -= 60; h++; } while (h < 0) { h += 24; d--; } while (h > 23) { h -= 24; d++; } while (d < 0) { m--; if (m < 0) { /* NB already adjusted m once above */ m += 12; y--; } d += monthlength[m] + (m == FEB && IS_LEAPYEAR(y)); } while (d > (int)(monthlength[m]) + (m == FEB && IS_LEAPYEAR(y))) { d -= monthlength[m] + (m == FEB && IS_LEAPYEAR(y)); m++; if (m > 11) { m -= 12; y++; } } *date = TIMECODE(DATE2CODE(y, m, d), h, n); #ifndef NOGMTIME if (unixtime) *date -= 1440; /* Unix time is one day different from analog time */ #endif return(OK); } choice wantdate(timecode_t *timecode, Dateman *dman, unsigned int hr, unsigned int min, unsigned int date, unsigned int month, unsigned int year, int tz) { static unsigned int oldy = 0, oldm = 0, oldd = 0, oldh = 25; static timecode_t oldtime = 0; /* first check integrity of date */ if (month > 11 || hr > 23 || min > 59 || date == 0 || date > monthlength[month] + (month == FEB && IS_LEAPYEAR(year)) || year < 1970 || year > 2069) return(ERR); if (hr != oldh || date != oldd || month != oldm || year != oldy) { oldtime = TIMECODE(DATE2CODE(year, month, date), hr, 0); oldh = hr; /* only calculate once an hour */ oldd = date; oldm = month; oldy = year; } *timecode = oldtime + min + tz; if (*timecode < dman->from || *timecode > dman->to) return(FALSE); return(TRUE); } choice wantunixtime(timecode_t *timecode, Dateman *dman, unsigned long unixtime, int tz) { /* no need to check range because parseulong() catches it */ *timecode = UXTIME2CODE(unixtime) + tz; if (*timecode < dman->from || *timecode > dman->to) return(FALSE); return(TRUE); } Daysdata *newday(unsigned int granularity) { extern Memman *xmemman; Daysdata *dp; unsigned int i; dp = (Daysdata *)submalloc(xmemman, sizeof(Daysdata)); dp->reqs = (unsigned long *)submalloc(xmemman, granularity * sizeof(unsigned long)); dp->pages = (unsigned long *)submalloc(xmemman, granularity * sizeof(unsigned long)); dp->bytes = (double *)submalloc(xmemman, granularity * sizeof(double)); for (i = 0; i < granularity; i++) { dp->reqs[i] = 0; dp->pages[i] = 0; dp->bytes[i] = 0.0; } dp->prev = NULL; dp->next = NULL; return(dp); } void dmaninit(Dateman *dman, datecode_t datecode, unsigned int granularity) { dman->firstdate = datecode; dman->lastdate = datecode; dman->currdate = datecode; dman->firstdp = newday(granularity); dman->lastdp = dman->firstdp; dman->currdp = dman->firstdp; } void datehash(timecode_t timecode, Dateman *dman, unsigned long reqs, unsigned long pages, double bytes, unsigned int granularity) { int i; Daysdata *dp; datecode_t datecode = (datecode_t)(timecode/1440); if (dman->currdp == NULL) dmaninit(dman, datecode, granularity); if (datecode >= dman->lastdate) { for (i = datecode - dman->lastdate; i > 0; i--) { dp = newday(granularity); dman->lastdp->next = dp; dp->prev = dman->lastdp; dman->lastdp = dp; dman->lastdate = datecode; } dman->currdp = dman->lastdp; } else if (datecode >= dman->currdate) { for (i = datecode - dman->currdate; i > 0; i--) dman->currdp = dman->currdp->next; } else if (datecode <= dman->firstdate) { for (i = dman->firstdate - datecode; i > 0; i--) { dp = newday(granularity); dman->firstdp->prev = dp; dp->next = dman->firstdp; dman->firstdp = dp; dman->firstdate = datecode; } dman->currdp = dman->firstdp; } else { dman->currdp = dman->firstdp; for (i = datecode - dman->firstdate; i > 0; i--) { dman->currdp = dman->currdp->next; } } dman->currdate = datecode; i = ((timecode % 1440) * granularity) / 1440; dman->currdp->reqs[i] += reqs; dman->currdp->pages[i] += pages; dman->currdp->bytes[i] += bytes; }