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