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