1 /* $Id: date.c,v 1.3 2002/03/02 21:31:53 sverrehu Exp $ */
2 /*------------------------------------------------------------------------
3  |  FILE            date.c
4  |  MODULE OF       biorythm - simple biorythm calculation program
5  |
6  |  DESCRIPTION     Routines handling dates.
7  |
8  |  WRITTEN BY      Sverre H. Huseby <shh@thathost.com>
9  +----------------------------------------------------------------------*/
10 
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 
15 #include <shhmsg.h>
16 
17 #include "date.h"
18 
19 /*-----------------------------------------------------------------------+
20 |  PUBLIC FUNCTIONS                                                      |
21 +-----------------------------------------------------------------------*/
22 
23 /*------------------------------------------------------------------------
24  |  NAME          dateSet
25  |
26  |  FUNCTION      Set the elements of a date.
27  |
28  |  INPUT         year    the year (full year, like 1996).
29  |                month   the month (1-12).
30  |                day     the day (1-31).
31  |
32  |  OUTPUT        date    the filled in date.
33  */
34 void
dateSet(Date * date,int year,int month,int day)35 dateSet(Date *date, int year, int month, int day)
36 {
37     date->year = year;
38     date->month = month;
39     date->day = day;
40 }
41 
42 /*------------------------------------------------------------------------
43  |  NAME          dateGet
44  |
45  |  FUNCTION      Get the elements of a date.
46  |
47  |  INPUT         date    the filled in date.
48  |
49  |  OUTPUT        year    the year (full year, like 1996).
50  |                month   the month (1-12).
51  |                day     the day (1-31).
52  */
53 void
dateGet(Date date,int * year,int * month,int * day)54 dateGet(Date date, int *year, int *month, int *day)
55 {
56     *year = date.year;
57     *month = date.month;
58     *day = date.day;
59 }
60 
61 /*------------------------------------------------------------------------
62  |  NAME          dateIsLeapYear
63  |
64  |  FUNCTION      Determine whether a given year is a leapyear.
65  |
66  |  INPUT         year    the year to test.
67  |
68  |  RETURNS       boolean value. 1=leapyear.
69  */
70 int
dateIsLeapYear(int year)71 dateIsLeapYear(int year)
72 {
73     return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
74 }
75 
76 /*------------------------------------------------------------------------
77  |  NAME          dateDaysInMonth
78  |
79  |  FUNCTION      Get number of days in a month.
80  |
81  |  INPUT         month   the month; (1..12)
82  |                year    the year to test. (full year, like 1996)
83  |
84  |  RETURNS       boolean value. 1=leapyear.
85  */
86 int
dateDaysInMonth(int month,int year)87 dateDaysInMonth(int month, int year)
88 {
89     static int vec[] = { -1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
90 
91     if (month == 2)
92 	return 28 + dateIsLeapYear(year);
93     return vec[month];
94 }
95 
96 /*------------------------------------------------------------------------
97  |  NAME          dateGetNumDays
98  |
99  |  FUNCTION      Get number of days from one date to another.
100  |
101  |  INPUT         d0      the start date.
102  |                d1      the end date.
103  |
104  |  RETURNS       Number of days. Examples:
105  |                  days are equal; returns 0.
106  |                  d1 is the day after d0; returns 1.
107  |                  d1 is the before after d0; returns -1.
108  */
109 int
dateGetNumDays(Date d0,Date d1)110 dateGetNumDays(Date d0, Date d1)
111 {
112     int q, negate = 0, ret = 0;
113     Date tmp;
114 
115     if (d0.year > d1.year
116 	|| (d0.year == d1.year && d0.month > d1.month)
117 	|| (d0.year == d1.year && d0.month == d1.month && d0.day > d1.day)) {
118         negate = 1;
119 	tmp = d0;
120 	d0 = d1;
121 	d1 = tmp;
122     }
123 
124     /* Count the days in all years _between_ the two given years. */
125     for (q = d0.year + 1; q < d1.year; q++)
126 	ret += 365 + dateIsLeapYear(q);
127 
128     /* */
129     if (d0.year != d1.year) {
130 	/* The years are not the same. */
131 	/* Add number of days left in first year. */
132 	ret += dateDaysInMonth(d0.month, d0.year) - d0.day;
133 	for (q = d0.month + 1; q <= 12; q++)
134 	    ret += dateDaysInMonth(q, d0.year);
135 	/* Add number of days `used' in second year. */
136 	for (q = 1; q < d1.month; q++)
137 	    ret += dateDaysInMonth(q, d1.year);
138 	ret += d1.day;
139     } else {
140 	/* The years are the same. */
141 	/* Count the days in all months _between_ the two given months. */
142 	for (q = d0.month + 1; q < d1.month; q++)
143 	    ret += dateDaysInMonth(q, d0.year);
144 	/* Add the days in each month. */
145 	if (d0.month == d1.month)
146 	    ret += d1.day - d0.day;
147 	else {
148 	    ret += dateDaysInMonth(d0.month, d0.year) - d0.day;
149 	    ret += d1.day;
150 	}
151     }
152     return negate ? -ret : ret;
153 }
154 
155 /*------------------------------------------------------------------------
156  |  NAME          dateInc
157  |
158  |  FUNCTION      Increase a date.
159  |
160  |  INPUT         date    the date to increase.
161  |
162  |  OUTPUT        date    the ncreased date.
163  */
164 void
dateInc(Date * date)165 dateInc(Date *date)
166 {
167     if (date->day < dateDaysInMonth(date->month, date->year))
168 	++date->day;
169     else {
170 	date->day = 1;
171 	if (date->month < 12)
172 	    ++date->month;
173 	else {
174 	    date->month = 1;
175 	    ++date->year;
176 	}
177     }
178 }
179 
180 /*------------------------------------------------------------------------
181  |  NAME          dateStr
182  |
183  |  FUNCTION      Get a string representation of a date.
184  |
185  |  INPUT         date    the date to represent as a string.
186  |
187  |  RETURNS       The date as a string.
188  */
189 char *
dateStr(Date date)190 dateStr(Date date)
191 {
192     static char ret[21];
193 
194     sprintf(ret, "%4d-%02d-%02d", date.year, date.month, date.day);
195     return ret;
196 }
197 
198 /*------------------------------------------------------------------------
199  |  NAME          dateFromStr
200  |
201  |  FUNCTION      Convert a string to a date.
202  |
203  |  INPUT         s       a string of the form YYYY-MM-DD
204  |
205  |  RETURNS       The date. Aborts if the date is illegal.
206  */
207 Date
dateFromStr(const char * s)208 dateFromStr(const char *s)
209 {
210     Date ret;
211     char *p, *s2;
212 
213     if ((s2 = strdup(s)) == NULL)
214 	msgFatal("out of memory\n");
215 
216     if ((p = strtok(s2, "/-:.")) == NULL)
217 	msgFatal("%s is not on the form YYYY-MM-DD\n", s);
218     ret.year = atoi(p);
219 
220     if ((p = strtok(NULL, "/-:.")) == NULL)
221 	msgFatal("%s is not on the form YYYY-MM-DD\n", s);
222     ret.month = atoi(p);
223     if (ret.month < 1 || ret.month > 12)
224 	msgFatal("illegal month in %s (YYYY-MM-DD)\n", s);
225 
226     if ((p = strtok(NULL, "/-:.")) == NULL)
227 	msgFatal("%s is not on the form YYYY-MM-DD\n", s);
228     ret.day = atoi(p);
229     if (ret.day < 1 || ret.day > dateDaysInMonth(ret.month, ret.year))
230 	msgFatal("illegal day in %s (YYYY-MM-DD)\n", s);
231 
232     free(s2);
233     return ret;
234 }
235