abs() is part of C89.
[mmh] / sbr / dtime.c
1 /*
2 ** dtime.c -- time/date routines
3 **
4 ** This code is Copyright (c) 2002, by the authors of nmh.  See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
7 */
8
9 #include <h/mh.h>   /* for snprintf() */
10 #include <h/nmh.h>
11 #include <h/tws.h>
12 #include <time.h>
13
14 #if !defined(HAVE_STRUCT_TM_TM_GMTOFF)
15 extern long timezone;
16 #endif
17
18 /*
19 ** The number of days in the year, accounting for leap years
20 */
21 #define dysize(y) \
22         (((y) % 4) ? 365 : (((y) % 100) ? 366 : (((y) % 400) ? 365 : 366)))
23
24 char *tw_moty[] = {
25         "Jan", "Feb", "Mar", "Apr",
26         "May", "Jun", "Jul", "Aug",
27         "Sep", "Oct", "Nov", "Dec",
28         NULL
29 };
30
31 char *tw_dotw[] = {
32         "Sun", "Mon", "Tue",
33         "Wed", "Thu", "Fri",
34         "Sat", NULL
35 };
36
37 char *tw_ldotw[] = {
38         "Sunday", "Monday", "Tuesday",
39         "Wednesday", "Thursday", "Friday",
40         "Saturday",  NULL
41 };
42
43 struct zone {
44         char *std;
45         char *dst;
46         int shift;
47 };
48
49 static int dmsize[] = {
50         31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
51 };
52
53
54 /*
55 ** Get current time (adjusted for local time
56 ** zone and daylight savings time) expressed
57 ** as nmh "broken-down" time structure.
58 */
59
60 struct tws *
61 dlocaltimenow(void)
62 {
63         time_t clock;
64
65         time(&clock);
66         return dlocaltime(&clock);
67 }
68
69
70 /*
71 ** Take clock value and return pointer to nmh time structure
72 ** containing "broken-down" time.  The time is adjusted for
73 ** local time zone and daylight savings time.
74 */
75
76 struct tws *
77 dlocaltime(time_t *clock)
78 {
79         static struct tws tw;
80         struct tm *tm;
81
82         if (!clock)
83                 return NULL;
84
85         tm = localtime(clock);
86
87         tw.tw_sec  = tm->tm_sec;
88         tw.tw_min  = tm->tm_min;
89         tw.tw_hour = tm->tm_hour;
90         tw.tw_mday = tm->tm_mday;
91         tw.tw_mon  = tm->tm_mon;
92
93         /*
94          * tm_year is always "year - 1900".
95          * So we correct for this.
96          */
97         tw.tw_year = tm->tm_year + 1900;
98         tw.tw_wday = tm->tm_wday;
99         tw.tw_yday = tm->tm_yday;
100
101         tw.tw_flags = TW_NULL;
102         if (tm->tm_isdst)
103                 tw.tw_flags |= TW_DST;
104
105 #ifdef HAVE_STRUCT_TM_TM_GMTOFF
106         tw.tw_zone = tm->tm_gmtoff / 60;
107         if (tm->tm_isdst)  /* if DST is in effect */
108                 tw.tw_zone -= 60;  /* reset to normal offset */
109 #else
110         tzset();
111         tw.tw_zone = -(timezone / 60);
112 #endif
113
114         tw.tw_flags &= ~TW_SDAY;
115         tw.tw_flags |= TW_SEXP;
116         tw.tw_flags &= ~TW_SZONE;
117         tw.tw_flags |= TW_SZEXP;
118
119         tw.tw_clock = *clock;
120
121         return (&tw);
122 }
123
124
125 /*
126 ** Take clock value and return pointer to nmh time
127 ** structure containing "broken-down" time.  Time is
128 ** expressed in UTC (Coordinated Universal Time).
129 */
130
131 struct tws *
132 dgmtime(time_t *clock)
133 {
134         static struct tws tw;
135         struct tm *tm;
136
137         if (!clock)
138                 return NULL;
139
140         tm = gmtime(clock);
141
142         tw.tw_sec  = tm->tm_sec;
143         tw.tw_min  = tm->tm_min;
144         tw.tw_hour = tm->tm_hour;
145         tw.tw_mday = tm->tm_mday;
146         tw.tw_mon  = tm->tm_mon;
147
148         /*
149          * tm_year is always "year - 1900"
150          * So we correct for this.
151          */
152         tw.tw_year = tm->tm_year + 1900;
153         tw.tw_wday = tm->tm_wday;
154         tw.tw_yday = tm->tm_yday;
155
156         tw.tw_flags = TW_NULL;
157         if (tm->tm_isdst)
158                 tw.tw_flags |= TW_DST;
159
160         tw.tw_zone = 0;
161
162         tw.tw_flags &= ~TW_SDAY;
163         tw.tw_flags |= TW_SEXP;
164         tw.tw_flags &= ~TW_SZONE;
165         tw.tw_flags |= TW_SZEXP;
166
167         tw.tw_clock = *clock;
168
169         return (&tw);
170 }
171
172
173 /*
174 ** Using a nmh "broken-down" time structure,
175 ** produce a 26-byte date/time string, such as
176 **
177 **    Tue Jan 14 17:49:03 1992\n\0
178 */
179
180 char *
181 dctime(struct tws *tw)
182 {
183         static char buffer[26];
184
185         if (!tw)
186                 return NULL;
187
188         snprintf(buffer, sizeof(buffer), "%.3s %.3s %02d %02d:%02d:%02d %.4d\n",
189                 tw_dotw[tw->tw_wday], tw_moty[tw->tw_mon], tw->tw_mday,
190                 tw->tw_hour, tw->tw_min, tw->tw_sec,
191                 tw->tw_year < 100 ? tw->tw_year + 1900 : tw->tw_year);
192
193         return buffer;
194 }
195
196
197 /*
198 ** Produce a date/time string of the form
199 **
200 **    Mon, 16 Jun 1992 15:30:48 -700 (or)
201 **    Mon, 16 Jun 1992 15:30:48 EDT
202 **
203 ** for the current time, as specified by rfc822.
204 ** The first form is required by rfc1123.
205 */
206
207 char *
208 dtimenow(int alpha_timezone)
209 {
210         time_t clock;
211
212         time(&clock);
213         return dtime(&clock, alpha_timezone);
214 }
215
216
217 /*
218 ** Using a local calendar time value, produce
219 ** a date/time string of the form
220 **
221 **    Mon, 16 Jun 1992 15:30:48 -700  (or)
222 **    Mon, 16 Jun 1992 15:30:48 EDT
223 **
224 ** as specified by rfc822.  The first form is required
225 ** by rfc1123 for outgoing messages.
226 */
227
228 char *
229 dtime(time_t *clock, int alpha_timezone)
230 {
231         if (alpha_timezone)
232                 /* use alpha-numeric timezones */
233                 return dasctime(dlocaltime(clock), TW_NULL);
234         else
235                 /* use numeric timezones */
236                 return dasctime(dlocaltime(clock), TW_ZONE);
237 }
238
239
240 /*
241 ** Using a nmh "broken-down" time structure, produce
242 ** a date/time string of the form
243 **
244 **    Mon, 16 Jun 1992 15:30:48 -0700
245 **
246 ** as specified by rfc822 and rfc1123.
247 */
248
249 char *
250 dasctime(struct tws *tw, int flags)
251 {
252         char buffer[80];
253         static char result[80];
254
255         if (!tw)
256                 return NULL;
257
258         /* Display timezone if known */
259         if ((tw->tw_flags & TW_SZONE) == TW_SZNIL)
260                 result[0] = '\0';
261         else
262                 snprintf(result, sizeof(result), " %s", dtimezone(tw->tw_zone, tw->tw_flags | flags));
263
264         snprintf(buffer, sizeof(buffer), "%02d %s %0*d %02d:%02d:%02d%s",
265                 tw->tw_mday, tw_moty[tw->tw_mon],
266                 tw->tw_year < 100 ? 2 : 4, tw->tw_year,
267                 tw->tw_hour, tw->tw_min, tw->tw_sec, result);
268
269         if ((tw->tw_flags & TW_SDAY) == TW_SEXP)
270                 snprintf(result, sizeof(result), "%s, %s", tw_dotw[tw->tw_wday], buffer);
271         else
272                 if ((tw->tw_flags & TW_SDAY) == TW_SNIL)
273                         strncpy(result, buffer, sizeof(result));
274                 else
275                         snprintf(result, sizeof(result), "%s (%s)", buffer, tw_dotw[tw->tw_wday]);
276
277         return result;
278 }
279
280
281 /*
282 ** Get the timezone for given offset
283 */
284
285 char *
286 dtimezone(int offset, int flags)
287 {
288         int hours, mins;
289         static char buffer[10];
290
291         if (offset < 0) {
292                 mins = -((-offset) % 60);
293                 hours = -((-offset) / 60);
294         } else {
295                 mins = offset % 60;
296                 hours = offset / 60;
297         }
298
299         if (!(flags & TW_ZONE) && mins == 0) {
300                 tzset();
301                 return ((flags & TW_DST) ? tzname[1] : tzname[0]);
302         }
303
304 #ifdef ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST
305         if (flags & TW_DST)
306                 hours += 1;
307 #endif /* ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST */
308         snprintf(buffer, sizeof(buffer), "%s%02d%02d",
309                 offset < 0 ? "-" : "+", abs(hours), abs(mins));
310         return buffer;
311 }
312
313
314 /*
315 ** Convert nmh time structure for local "broken-down"
316 ** time to calendar time (clock value).  This routine
317 ** is based on the gtime() routine written by Steven Shafer
318 ** at CMU.  It was forwarded to MTR by Jay Lepreau at Utah-CS.
319 */
320
321 time_t
322 dmktime(struct tws *tw)
323 {
324         int i, sec, min, hour, mday, mon, year;
325         time_t result;
326
327         if (tw->tw_clock != 0)
328                 return tw->tw_clock;
329
330         if ((sec = tw->tw_sec) < 0 || sec > 61
331                 || (min = tw->tw_min) < 0 || min > 59
332                 || (hour = tw->tw_hour) < 0 || hour > 23
333                 || (mday = tw->tw_mday) < 1 || mday > 31
334                 || (mon = tw->tw_mon + 1) < 1 || mon > 12)
335                 return (tw->tw_clock = (time_t) -1);
336
337         year = tw->tw_year;
338
339         result = 0;
340         if (year < 1970)
341                 year += 1900;
342
343         if (year < 1970)
344                 year += 100;
345
346         for (i = 1970; i < year; i++)
347                 result += dysize(i);
348         if (dysize(year) == 366 && mon >= 3)
349                 result++;
350         while (--mon)
351                 result += dmsize[mon - 1];
352         result += mday - 1;
353         result = 24 * result + hour;
354         result = 60 * result + min;
355         result = 60 * result + sec;
356         result -= 60 * tw->tw_zone;
357         if (tw->tw_flags & TW_DST)
358                 result -= 60 * 60;
359
360         return (tw->tw_clock = result);
361 }
362
363
364 /*
365 ** Simple calculation of day of the week.  Algorithm
366 ** used is Zeller's congruence.  We assume that
367 ** if tw->tw_year < 100, then the century = 19.
368 */
369
370 void
371 set_dotw(struct tws *tw)
372 {
373         int month, day, year, century;
374
375         month = tw->tw_mon - 1;
376         day = tw->tw_mday;
377         year = tw->tw_year % 100;
378         century = tw->tw_year < 100 ? 19 : tw->tw_year / 100;
379
380         if (month <= 0) {
381                 month += 12;
382                 if (--year < 0) {
383                         year += 100;
384                         century--;
385                 }
386         }
387
388         tw->tw_wday = ((26 * month - 2) / 10 + day + year + year / 4
389                         - 3 * century / 4 + 1) % 7;
390         if (tw->tw_wday < 0)
391                 tw->tw_wday += 7;
392
393         tw->tw_flags &= ~TW_SDAY, tw->tw_flags |= TW_SIMP;
394 }
395
396
397 /*
398 ** Copy nmh time structure
399 */
400
401 void
402 twscopy(struct tws *tb, struct tws *tw)
403 {
404         *tb = *tw;  /* struct copy */
405 }
406
407
408 /*
409 ** Compare two nmh time structures
410 */
411
412 int
413 twsort(struct tws *tw1, struct tws *tw2)
414 {
415         time_t c1, c2;
416
417         if (tw1->tw_clock == 0)
418                 dmktime(tw1);
419         if (tw2->tw_clock == 0)
420                 dmktime(tw2);
421
422         return ((c1 = tw1->tw_clock) > (c2 = tw2->tw_clock) ? 1
423                 : c1 == c2 ? 0 : -1);
424 }