1/*
2 * Legal Notice
3 *
4 * This document and associated source code (the "Work") is a part of a
5 * benchmark specification maintained by the TPC.
6 *
7 * The TPC reserves all right, title, and interest to the Work as provided
8 * under U.S. and international laws, including without limitation all patent
9 * and trademark rights therein.
10 *
11 * No Warranty
12 *
13 * 1.1 TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE INFORMATION
14 * CONTAINED HEREIN IS PROVIDED "AS IS" AND WITH ALL FAULTS, AND THE
15 * AUTHORS AND DEVELOPERS OF THE WORK HEREBY DISCLAIM ALL OTHER
16 * WARRANTIES AND CONDITIONS, EITHER EXPRESS, IMPLIED OR STATUTORY,
17 * INCLUDING, BUT NOT LIMITED TO, ANY (IF ANY) IMPLIED WARRANTIES,
18 * DUTIES OR CONDITIONS OF MERCHANTABILITY, OF FITNESS FOR A PARTICULAR
19 * PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OF
20 * WORKMANLIKE EFFORT, OF LACK OF VIRUSES, AND OF LACK OF NEGLIGENCE.
21 * ALSO, THERE IS NO WARRANTY OR CONDITION OF TITLE, QUIET ENJOYMENT,
22 * QUIET POSSESSION, CORRESPONDENCE TO DESCRIPTION OR NON-INFRINGEMENT
23 * WITH REGARD TO THE WORK.
24 * 1.2 IN NO EVENT WILL ANY AUTHOR OR DEVELOPER OF THE WORK BE LIABLE TO
25 * ANY OTHER PARTY FOR ANY DAMAGES, INCLUDING BUT NOT LIMITED TO THE
26 * COST OF PROCURING SUBSTITUTE GOODS OR SERVICES, LOST PROFITS, LOSS
27 * OF USE, LOSS OF DATA, OR ANY INCIDENTAL, CONSEQUENTIAL, DIRECT,
28 * INDIRECT, OR SPECIAL DAMAGES WHETHER UNDER CONTRACT, TORT, WARRANTY,
29 * OR OTHERWISE, ARISING IN ANY WAY OUT OF THIS OR ANY OTHER AGREEMENT
30 * RELATING TO THE WORK, WHETHER OR NOT SUCH AUTHOR OR DEVELOPER HAD
31 * ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES.
32 *
33 * Contributors:
34 * Gradient Systems
35 */
36
37/*** includes ***/
38#include "config.h"
39#include "porting.h"
40#include <stdlib.h>
41#ifndef USE_STDLIB_H
42#include <malloc.h>
43#endif
44#include <stdio.h>
45#include <math.h>
46#include "date.h"
47#include "mathops.h"
48#include "dist.h"
49
50#define D_CHARS "ymdYMD24" /* valid characters in a DBGDATE setting */
51#define MIN_DATE_INT 18000101
52
53static int m_days[2][13] = {{0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
54 {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}};
55static char *qtr_start[5] = {NULL, "01-01", "04-01", "07-01", "10-01"};
56char *weekday_names[8] = {NULL, "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
57/*
58 * Routine: mk_date(void)
59 * Purpose: initialize a date_t
60 * Algorithm:
61 * Data Structures:
62 * Params:
63 * Returns: date_t *
64 * Called By:
65 * Calls:
66 * Assumptions:
67 * Side Effects:
68 * TODO: None
69 */
70date_t *mk_date(void) {
71 date_t *res;
72
73 res = (date_t *)malloc(sizeof(struct DATE_T));
74 MALLOC_CHECK(res);
75
76 res->flags = 0;
77 res->year = 0;
78 res->month = 0;
79 res->day = 0;
80 res->julian = 0;
81
82 return (res);
83}
84/*
85 * Routine: strtotime(char *str)
86 * Purpose: convert a string from the time to the number of seconds since
87 * midnight Algorithm: Data Structures: Params: Returns: int Called By: Calls:
88 * Assumptions:
89 * Side Effects:
90 * TODO: None
91 */
92int strtotime(char *str) {
93 int hour, min, sec, res;
94
95 if (sscanf(str, "%d:%d:%d", &hour, &min, &sec) != 3) {
96 if (sscanf(str, "%d:%d", &hour, &min) != 2) {
97 INTERNAL("Invalid time format");
98 }
99 sec = 0;
100 }
101
102 if (hour > 23 || hour < 0)
103 INTERNAL("Invalid time format");
104 if (min > 59 || min < 0)
105 INTERNAL("Invalid time format");
106 if (sec > 59 || sec < 0)
107 INTERNAL("Invalid time format");
108
109 res = hour * 3600 + min * 60 + sec;
110
111 return (res);
112}
113
114/*
115 * Routine: jtodt(int src, date_t *dest)
116 * Purpose: convert a number of julian days to a date_t
117 * Algorithm: Fleigel and Van Flandern (CACM, vol 11, #10, Oct. 1968, p. 657)
118 * Data Structures:
119 *
120 * Params: source integer: days since big bang
121 * Returns: date_t *; NULL on failure
122 * Called By:
123 * Calls:
124 * Assumptions:
125 * Side Effects:
126 * TODO:
127 */
128int jtodt(date_t *dest, int src) {
129 long i, j, l, n;
130
131 if (src < 0)
132 return (-1);
133
134 dest->julian = src;
135 l = src + 68569;
136 n = (int)floor((4 * l) / 146097);
137 l = l - (int)floor((146097 * n + 3) / 4);
138 i = (int)floor((4000 * (l + 1) / 1461001));
139 l = l - (int)floor((1461 * i) / 4) + 31;
140 j = (int)floor((80 * l) / 2447);
141 dest->day = l - (int)floor((2447 * j) / 80);
142 l = (int)floor(j / 11);
143 dest->month = j + 2 - 12 * l;
144 dest->year = 100 * (n - 49) + i + l;
145
146 return (0);
147}
148
149/*
150 * Routine: dttoj(date_t *)
151 * Purpose: convert a date_t to a number of julian days
152 * Algorithm: http://quasar.as.utexas.edu/BillInfo/JulianDatesG.html
153 * Data Structures:
154 *
155 * Params:
156 * Returns:
157 * Called By:
158 * Calls:
159 * Assumptions:
160 * Side Effects:
161 * TODO: None
162 */
163int dttoj(date_t *dt) {
164 int y, m, res;
165
166 y = dt->year;
167 m = dt->month;
168 if (m <= 2) {
169 m += 12;
170 y -= 1;
171 }
172
173 /*
174 * added 1 to get dttoj and jtodt to match
175 */
176 res = dt->day + (153 * m - 457) / 5 + 365 * y + (int)floor(y / 4) - (int)floor(y / 100) + (int)floor(y / 400) +
177 1721118 + 1;
178
179 return (res);
180}
181
182/*
183 * Routine: strtodt()
184 * Purpose: Convert an ascii string to a date_t structure
185 * Algorithm:
186 * Data Structures:
187 *
188 * Params: char *s, date_t *dest
189 * Returns: int; 0 on success
190 * Called By:
191 * Calls:
192 * Assumptions:
193 * Side Effects:
194 * TODO: Need to allow for date formats other than Y4MD-
195 */
196int strtodt(date_t *dest, char *s) {
197 int nRetCode = 0;
198
199 if (s == NULL) {
200 dest = NULL;
201 return (-1);
202 }
203
204 if (sscanf(s, "%4d-%d-%d", &dest->year, &dest->month, &dest->day) != 3) {
205 fprintf(stderr, "ERROR: Invalid string to date conversion in strtodt\n");
206 nRetCode = -1;
207 }
208
209 dest->julian = dttoj(dest);
210
211 return (nRetCode);
212}
213
214/*
215 * Routine: dttostr(date_t *d)
216 * Purpose: convert a date_t structure to a string
217 * Algorithm:
218 * Data Structures:
219 *
220 * Params:
221 * Returns: char *; NULL on failure
222 * Called By:
223 * Calls:
224 * Assumptions:
225 * Side Effects:
226 * TODO: 20000110 Need to handle more than Y4MD-
227 */
228char *dttostr(date_t *d) {
229 static char *res;
230 static int init = 0;
231
232 if (!init) {
233 res = (char *)malloc(sizeof(char) * 11);
234 MALLOC_CHECK(res);
235 init = 1;
236 }
237
238 if (d == NULL)
239 return (NULL);
240
241 sprintf(res, "%4d-%02d-%02d", d->year, d->month, d->day);
242
243 return (res);
244}
245
246/*
247 * Routine: date_init
248 * Purpose: set the date handling parameters
249 * Algorithm:
250 * Data Structures:
251 *
252 * Params: None
253 * Returns: int; 0 on success
254 * Called By:
255 * Calls:
256 * Assumptions:
257 * Side Effects:
258 * TODO: None
259 */
260int date_init(void) {
261 printf("date_init is not yet complete\n");
262 exit(1);
263 return (0);
264}
265
266/*
267 * Routine: date_t_op(int op, date_t *operand1, date_t *operand2)
268 * Purpose: execute arbitrary binary operations on date_t's
269 * Algorithm:
270 * Data Structures:
271 *
272 * Params:
273 * Returns:
274 * Called By:
275 * Calls:
276 * Assumptions:
277 * Side Effects:
278 * TODO:
279 * 20010806 jms Return code is meaningless
280 */
281int date_t_op(date_t *dest, int op, date_t *d1, date_t *d2) {
282 int tJulian;
283 char tString[11];
284 date_t tDate;
285
286 switch (op) {
287 case OP_FIRST_DOM: /* set to first day of month */
288 tJulian = d1->julian - d1->day + 1;
289 jtodt(dest, tJulian);
290 break;
291 case OP_LAST_DOM: /* set to last day of month */
292 tJulian = d1->julian - d1->day + m_days[is_leap(d1->year)][d1->month];
293 jtodt(dest, tJulian);
294 break;
295 case OP_SAME_LY:
296 if (is_leap(d1->year) && (d1->month == 2) && (d1->day == 29))
297 sprintf(tString, "%d-02-28", d1->year - 1);
298 else
299 sprintf(tString, "%4d-%02d-%02d", d1->year - 1, d1->month, d1->day);
300 strtodt(dest, tString);
301 break;
302 case OP_SAME_LQ:
303 switch (d1->month) {
304 case 1:
305 case 2:
306 case 3:
307 sprintf(tString, "%4d-%s", d1->year, qtr_start[1]);
308 strtodt(&tDate, tString);
309 tJulian = d1->julian - tDate.julian;
310 sprintf(tString, "%4d-%s", d1->year - 1, qtr_start[4]);
311 strtodt(&tDate, tString);
312 tJulian += tDate.julian;
313 jtodt(dest, tJulian);
314 break;
315 case 4:
316 case 5:
317 case 6:
318 sprintf(tString, "%4d-%s", d1->year, qtr_start[2]);
319 strtodt(&tDate, tString);
320 tJulian = d1->julian - tDate.julian;
321 sprintf(tString, "%4d-%s", d1->year, qtr_start[1]);
322 strtodt(&tDate, tString);
323 tJulian += tDate.julian;
324 jtodt(dest, tJulian);
325 break;
326 case 7:
327 case 8:
328 case 9:
329 sprintf(tString, "%4d-%s", d1->year, qtr_start[3]);
330 strtodt(&tDate, tString);
331 tJulian = d1->julian - tDate.julian;
332 sprintf(tString, "%4d-%s", d1->year, qtr_start[2]);
333 strtodt(&tDate, tString);
334 tJulian += tDate.julian;
335 jtodt(dest, tJulian);
336 break;
337 case 10:
338 case 11:
339 case 12:
340 sprintf(tString, "%4d-%s", d1->year, qtr_start[4]);
341 strtodt(&tDate, tString);
342 tJulian = d1->julian - tDate.julian;
343 sprintf(tString, "%4d-%s", d1->year, qtr_start[3]);
344 strtodt(&tDate, tString);
345 tJulian += tDate.julian;
346 jtodt(dest, tJulian);
347 break;
348 }
349 break;
350 }
351
352 return (0);
353}
354
355/*
356 * Routine: itodt(date_t *d, int src)
357 * Purpose: convert a number of days to a date_t
358 * Algorithm: NOTE: sets only julian field
359 * Data Structures:
360 *
361 * Params:
362 * Returns:
363 * Called By:
364 * Calls:
365 * Assumptions:
366 * Side Effects:
367 * TODO: None
368 */
369int itodt(date_t *dest, int src) {
370
371 dest->julian = src;
372
373 return (0);
374}
375
376/*
377 * Routine: set_dow(date *d)
378 * Purpose: perpetual calendar stuff
379 * Algorithm:
380 * Data Structures:
381 *
382 * Params:
383 * Returns:
384 * Called By:
385 * Calls:
386 * Assumptions:
387 * Side Effects:
388 * TODO:
389 */
390static int doomsday[4] = {3, 2, 0, 5};
391static int known[13] = {0, 3, 0, 0, 4, 9, 6, 11, 8, 5, 10, 7, 12};
392int set_dow(date_t *d) {
393
394 static int last_year = -1, dday;
395 int res, q, r, s;
396
397 if (d->year != last_year) {
398 if (is_leap(d->year)) {
399 /* adjust the known dates for january and february */
400 known[1] = 4;
401 known[2] = 1;
402 } else {
403 known[1] = 3;
404 known[2] = 0;
405 }
406
407 /* calculate the doomsday for the century */
408 dday = d->year / 100;
409 dday -= 15;
410 dday %= 4;
411 dday = doomsday[dday];
412
413 /* and then calculate the doomsday for the year */
414 q = d->year % 100;
415 r = q % 12;
416 q /= 12;
417 s = r / 4;
418 dday += q + r + s;
419 dday %= 7;
420 last_year = d->year;
421 }
422
423 res = d->day;
424 res -= known[d->month];
425 while (res < 0)
426 res += 7;
427 while (res > 6)
428 res -= 7;
429
430 res += dday;
431 res %= 7;
432
433 return (res);
434}
435
436/*
437 * Routine: is_leap(year)
438 * Purpose:
439 * Algorithm:
440 * Data Structures:
441 *
442 * Params:
443 * Returns:
444 * Called By:
445 * Calls:
446 * Assumptions:
447 * Side Effects:
448 * TODO: None
449 */
450int is_leap(int year) {
451
452 return (((year % 100) == 0) ? ((((year % 400) % 2) == 0) ? 1 : 0) : ((year % 4) == 0) ? 1 : 0);
453}
454
455/*
456 * Routine: day_number(date_t *)
457 * Purpose:
458 * Algorithm: NOTE: this is NOT the ordinal day in the year, but the ordinal
459 *reference into the calendar distribution for the day; in particular, this
460 *needs to skip over the leap day Data Structures:
461 *
462 * Params:
463 * Returns:
464 * Called By:
465 * Calls:
466 * Assumptions:
467 * Side Effects:
468 * TODO: None
469 */
470int day_number(date_t *d) {
471 return (m_days[is_leap(d->year)][d->month] + d->day);
472}
473
474/*
475 * Routine: getDateWeightFromJulian(jDay, nDistribution)
476 * Purpose: return the weight associated with a particular julian date and
477 * distribution Algorithm: Data Structures:
478 *
479 * Params:
480 * Returns:
481 * Called By:
482 * Calls:
483 * Assumptions:
484 * Side Effects:
485 * TODO: None
486 */
487int getDateWeightFromJulian(jDay, nDistribution) {
488 date_t dTemp;
489 int nDay;
490
491 jtodt(&dTemp, jDay);
492 nDay = day_number(&dTemp);
493
494 return (dist_weight(NULL, "calendar", nDay, nDistribution + is_leap(dTemp.year)));
495}
496
497/*
498 * Routine: date_part(date_t *, int part)
499 * Purpose:
500 * Algorithm:
501 * Data Structures:
502 *
503 * Params:
504 * Returns:
505 * Called By:
506 * Calls:
507 * Assumptions:
508 * Side Effects:
509 * TODO: None
510 */
511int date_part(date_t *d, int part) {
512 switch (part) {
513 case 1:
514 return (d->year);
515 case 2:
516 return (d->month);
517 case 3:
518 return (d->day);
519 default:
520 INTERNAL("Invalid call to date_part()");
521 return (-1);
522 }
523}
524
525#ifdef TEST
526main() {
527 date_t *d;
528 int ret;
529
530 d = mk_date();
531 strtodt(d, "1776-07-04");
532 ret = set_dow(d);
533 printf("set_dow(\"1776-07-04\"): wanted 4 got %d\n", ret);
534 if (ret != 4) {
535 exit(1);
536 }
537 strtodt(d, "2000-01-01");
538 ret = set_dow(d);
539 printf("set_dow(\"2000-01-01\"): wanted 6 got %d\n", ret);
540 if (ret != 6) {
541 exit(1);
542 }
543
544 strtodt(d, "1970-01-01");
545 if ((ret = dttoj(d)) != 2440588) {
546 printf("dttoj returned %d\n", ret);
547 exit(1);
548 }
549
550 d->year = 1;
551 d->month = 11;
552 d->date = 11;
553 jtodt(d, 2440588);
554 if ((d->year != 1970) || (d->month != 1) || (d->date != 1)) {
555 printf("jtodt failed got: ");
556 printf("%4d-%02d-%02d", d->year, d->month, d->date);
557 exit(1);
558 }
559 return (0);
560}
561#endif /* TEST */
562