1 /***             analog 6.0             http://www.analog.cx/             ***/
2 /*** This program is copyright (c) Stephen R. E. Turner 1995 - 2004 except as
3  *** stated otherwise.
4  ***
5  *** This program is free software. You can redistribute it and/or modify it
6  *** under the terms of version 2 of the GNU General Public License, which you
7  *** should have received with it.
8  ***
9  *** This program is distributed in the hope that it will be useful, but
10  *** without any warranty, expressed or implied.   ***/
11 
12 /*** dates.c; anything to do with dates. ***/
13 
14 #include "anlghea3.h"
15 
16 /* Analog stores times internally in minutes since midnight 30-31/12/1969.
17    (Not in "Unix time" because it works better if all valid times are > 0).
18    Note also that months are numbered Jan = 0 to Dec = 11 internally. */
19 
20 unsigned int daysbefore[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273,
21 			       304, 334};  /* in non-leap year */
22 unsigned int monthlength[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,
23 				31};
24 
code2date(datecode_t code,unsigned int * date,unsigned int * month,unsigned int * year)25 void code2date(datecode_t code, unsigned int *date, unsigned int *month,
26 	       unsigned int *year) {
27   /* not most efficient possible, but only used in output so doesn't need to
28      be super-fast. NB DATE2CODE() is in anlghea3.h. */
29   code += 364;    /* so 0 -> 1/1/69 */
30   *year = 1969 + 4 * (code / 1461);
31   code %= 1461;
32   *year += MIN(code / 365, 3); /* no leap days for a while after 1/1/69! */
33   if (code == 1460) {
34     *month = DEC;
35     *date = 31;
36   }
37   else {
38     code %= 365;    /* so 0 -> 1/Jan, any year */
39     for (*month = DEC;
40 	 daysbefore[*month] + (IS_LEAPYEAR(*year) && *month > FEB) > code;
41 	 (*month)--)
42       ;   /* run to right month */
43     *date = code - daysbefore[*month] + 1 -
44       (IS_LEAPYEAR(*year) && *month > FEB);
45   }
46 }
47 
shifttime(time_t timer,int diff)48 time_t shifttime(time_t timer, int diff) {
49   /* Surprisingly, there is no ANSI C function to do this */
50   struct tm *lt = localtime(&timer);
51   lt->tm_min += diff;
52   timer = mktime(lt);
53   return(timer);
54 }
55 
parsedate(time_t starttime,char * s,timecode_t * date,logical from,logical unixtime)56 choice parsedate(time_t starttime, char *s, timecode_t *date, logical from,
57 		 logical unixtime) {
58   struct tm *st;
59   int y, m, d, h, n;
60   char *p;
61 
62 #ifndef NOGMTIME
63   if (unixtime)
64     st = gmtime(&starttime);
65   else
66 #endif
67     st = localtime(&starttime);
68   if (ISDIGIT(*s) && ISDIGIT(*(s + 1))) {
69     y = 10 * (*s - '0') + (*(s + 1) - '0');
70     s += 2;
71     y += 1900;
72     if (y < 1970)
73       y += 100;
74   }
75   else if (*s == '+' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) {
76     y = st->tm_year + 1900 + 10 * (*(s + 1) - '0') + (*(s + 2) - '0');
77     s += 3;
78   }
79   else if (*s == '-' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) {
80     y = st->tm_year + 1900 - 10 * (*(s + 1) - '0') - (*(s + 2) - '0');
81     s += 3;
82   }
83   else
84     return(ERR);
85 
86   if (ISDIGIT(*s) && ISDIGIT(*(s + 1))) {
87     m = 10 * (*s - '0') + (*(s + 1) - '0') - 1;
88     if (m > 11 || m < 0)
89       return(ERR);
90     s += 2;
91   }
92   else if (*s == '+' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) {
93     m = st->tm_mon + 10 * (*(s + 1) - '0') + (*(s + 2) - '0');
94     s += 3;
95   }
96   else if (*s == '-' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) {
97     m = st->tm_mon - 10 * (*(s + 1) - '0') - (*(s + 2) - '0');
98     s += 3;
99   }
100   else
101     return(ERR);
102   while (m < 0) {   /* need to do this now because about to use monthlength */
103     m += 12;
104     y--;
105   }
106   while (m > 11) {
107     m -= 12;
108     y++;
109   }
110 
111   if (ISDIGIT(*s) && ISDIGIT(*(s + 1))) {
112     d = (int)strtol(s, &p, 10);
113     if (d > 31 || d <= 0)
114       return(ERR);
115     else if (d > (int)(monthlength[m]) + (m == FEB && IS_LEAPYEAR(y)))
116       d = monthlength[m] + (m == FEB && IS_LEAPYEAR(y));
117   }                   /* relative dates must be >= 2 digits but can be more */
118   else if (*s == '+' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2)))
119     d = st->tm_mday + (int)strtol(s + 1, &p, 10);
120   else if (*s == '-' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2)))
121     d = st->tm_mday - (int)strtol(s + 1, &p, 10);
122   else
123     return(ERR);
124 
125   if (*p == ':') {  /* parse hour & minute */
126     s = p + 1;
127     if (ISDIGIT(*s) && ISDIGIT(*(s + 1))) {
128       h = 10 * (*s - '0') + (*(s + 1) - '0');
129       if (h > 23)
130 	return(ERR);
131       s += 2;
132     }
133     else if (*s == '+' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) {
134       h = st->tm_hour + 10 * (*(s + 1) - '0') + (*(s + 2) - '0');
135       s += 3;
136     }
137     else if (*s == '-' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) {
138       h = st->tm_hour - 10 * (*(s + 1) - '0') - (*(s + 2) - '0');
139       s += 3;
140     }
141     else
142       return(ERR);
143 
144     if (ISDIGIT(*s) && ISDIGIT(*(s + 1))) {
145       n = 10 * (*s - '0') + (*(s + 1) - '0');
146       if (n > 59)
147 	return(ERR);
148       s += 2;
149     }
150     else if (*s == '+' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) {
151       n = st->tm_min + 10 * (*(s + 1) - '0') + (*(s + 2) - '0');
152       s += 3;
153     }
154     else if (*s == '-' && ISDIGIT(*(s + 1)) && ISDIGIT(*(s + 2))) {
155       n = st->tm_min - 10 * (*(s + 1) - '0') - (*(s + 2) - '0');
156       s += 3;
157     }
158     else
159       return(ERR);
160 
161     if (*s != '\0')
162       return(ERR);
163   }                 /* end *p == ':' */
164   else if (*p == '\0' || *p == 'd' || *p == 'D' || *p == 'e' || *p == 'E') {
165     /* d, e can come from FLOOR */
166     if (from) {
167       h = 0;
168       n = 0;
169     }
170     else {
171       h = 23;
172       n = 59;
173     }
174   }
175   else
176     return(ERR);
177 
178   while (n < 0) {
179     n += 60;
180     h--;
181   }
182   while (n > 59) {
183     n -= 60;
184     h++;
185   }
186   while (h < 0) {
187     h += 24;
188     d--;
189   }
190   while (h > 23) {
191     h -= 24;
192     d++;
193   }
194   while (d < 0) {
195     m--;
196     if (m < 0) {   /* NB already adjusted m once above */
197       m += 12;
198       y--;
199     }
200     d += monthlength[m] + (m == FEB && IS_LEAPYEAR(y));
201   }
202   while (d > (int)(monthlength[m]) + (m == FEB && IS_LEAPYEAR(y))) {
203     d -= monthlength[m] + (m == FEB && IS_LEAPYEAR(y));
204     m++;
205     if (m > 11) {
206       m -= 12;
207       y++;
208     }
209   }
210 
211   *date = TIMECODE(DATE2CODE(y, m, d), h, n);
212 #ifndef NOGMTIME
213   if (unixtime)
214     *date -= 1440;   /* Unix time is one day different from analog time */
215 #endif
216   return(OK);
217 }
218 
wantdate(timecode_t * timecode,Dateman * dman,unsigned int hour,unsigned int minute,unsigned int date,unsigned int month,unsigned int year,int tz)219 choice wantdate(timecode_t *timecode, Dateman *dman, unsigned int hour,
220 		unsigned int minute, unsigned int date, unsigned int month,
221 		unsigned int year, int tz) {
222   static unsigned int oldy = 0, oldm = 0, oldd = 0, oldh = 25;
223   static timecode_t oldtime = 0;
224 
225   /* first check integrity of date */
226   if (month > 11 || hour > 23 || minute > 59 || date == 0 ||
227       date > monthlength[month] + (month == FEB && IS_LEAPYEAR(year)) ||
228       year < 1970 || year > 2069)
229     return(ERR);
230 
231   if (hour != oldh || date != oldd || month != oldm || year != oldy) {
232     oldtime = TIMECODE(DATE2CODE(year, month, date), hour, 0);
233     oldh = hour;                        /* only calculate once an hour */
234     oldd = date;
235     oldm = month;
236     oldy = year;
237   }
238 
239   *timecode =  oldtime + minute + tz;
240   if (*timecode < dman->from || *timecode > dman->to)
241     return(FALSE);
242   return(TRUE);
243 }
244 
wantunixtime(timecode_t * timecode,Dateman * dman,unsigned long unixtime,int tz)245 choice wantunixtime(timecode_t *timecode, Dateman *dman,
246 		    unsigned long unixtime, int tz) {
247   /* no need to check range because parseunixtime() catches it */
248   *timecode = UXTIME2CODE(unixtime) + tz;
249   if (*timecode < dman->from || *timecode > dman->to)
250     return(FALSE);
251   return(TRUE);
252 }
253 
newday(unsigned int granularity)254 Daysdata *newday(unsigned int granularity) {
255   extern Memman *xmemman;
256 
257   Daysdata *dp;
258   unsigned int i;
259 
260   dp = (Daysdata *)submalloc(xmemman, sizeof(Daysdata));
261   dp->reqs = (unsigned long *)submalloc(xmemman,
262 					granularity * sizeof(unsigned long));
263   dp->pages = (unsigned long *)submalloc(xmemman,
264 					 granularity * sizeof(unsigned long));
265   dp->bytes = (double *)submalloc(xmemman, granularity * sizeof(double));
266 
267   for (i = 0; i < granularity; i++) {
268     dp->reqs[i] = 0;
269     dp->pages[i] = 0;
270     dp->bytes[i] = 0.0;
271   }
272 
273   dp->prev = NULL;
274   dp->next = NULL;
275 
276   return(dp);
277 }
278 
dmaninit(Dateman * dman,datecode_t datecode,unsigned int granularity)279 void dmaninit(Dateman *dman, datecode_t datecode, unsigned int granularity) {
280   dman->firstdate = datecode;
281   dman->lastdate = datecode;
282   dman->currdate = datecode;
283   dman->firstdp = newday(granularity);
284   dman->lastdp = dman->firstdp;
285   dman->currdp = dman->firstdp;
286 }
287 
datehash(timecode_t timecode,Dateman * dman,unsigned long reqs,unsigned long pages,double bytes,unsigned int granularity)288 void datehash(timecode_t timecode, Dateman *dman, unsigned long reqs,
289 	      unsigned long pages, double bytes, unsigned int granularity) {
290   int i;
291   Daysdata *dp;
292 
293   datecode_t datecode = (datecode_t)(timecode/1440);
294 
295   if (dman->currdp == NULL)
296     dmaninit(dman, datecode, granularity);
297   if (datecode >= dman->lastdate) {
298     for (i = datecode - dman->lastdate; i > 0; i--) {
299       dp = newday(granularity);
300       dman->lastdp->next = dp;
301       dp->prev = dman->lastdp;
302       dman->lastdp = dp;
303       dman->lastdate = datecode;
304     }
305     dman->currdp = dman->lastdp;
306   }
307   else if (datecode >= dman->currdate) {
308     for (i = datecode - dman->currdate; i > 0; i--)
309       dman->currdp = dman->currdp->next;
310   }
311   else if (datecode <= dman->firstdate) {
312     for (i = dman->firstdate - datecode; i > 0; i--) {
313       dp = newday(granularity);
314       dman->firstdp->prev = dp;
315       dp->next = dman->firstdp;
316       dman->firstdp = dp;
317       dman->firstdate = datecode;
318     }
319     dman->currdp = dman->firstdp;
320   }
321   else {
322     dman->currdp = dman->firstdp;
323     for (i = datecode - dman->firstdate; i > 0; i--) {
324       dman->currdp = dman->currdp->next;
325     }
326   }
327 
328   dman->currdate = datecode;
329 
330   i = ((timecode % 1440) * granularity) / 1440;
331   dman->currdp->reqs[i] += reqs;
332   dman->currdp->pages[i] += pages;
333   dman->currdp->bytes[i] += bytes;
334 }
335