6acfaeb63477c51fb4d383003b6ccb1d33fd38e2
[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  * This used to return a three-letter abbreviation for some offset
285  * values.  But not many.  Until there's a good way to do that,
286  * return the string representation of the numeric offset.
287  */
288
289 char *
290 dtimezone (int offset, int flags)
291 {
292     int hours, mins;
293     static char buffer[10];
294
295     if (offset < 0) {
296         mins = -((-offset) % 60);
297         hours = -((-offset) / 60);
298     } else {
299         mins = offset % 60;
300         hours = offset / 60;
301     }
302
303 #ifdef ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST
304     if (flags & TW_DST)
305         hours += 1;
306 #endif /* ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST */
307     snprintf (buffer, sizeof(buffer), "%s%02d%02d",
308                 offset < 0 ? "-" : "+", abs (hours), abs (mins));
309     return buffer;
310 }
311
312
313 /*
314  * Convert nmh time structure for local "broken-down"
315  * time to calendar time (clock value).  This routine
316  * is based on the gtime() routine written by Steven Shafer
317  * at CMU.  It was forwarded to MTR by Jay Lepreau at Utah-CS.
318  */
319
320 time_t
321 dmktime (struct tws *tw)
322 {
323     int i, sec, min, hour, mday, mon, year;
324     time_t result;
325
326     if (tw->tw_clock != 0)
327         return tw->tw_clock;
328
329     if ((sec = tw->tw_sec) < 0 || sec > 61
330             || (min = tw->tw_min) < 0 || min > 59
331             || (hour = tw->tw_hour) < 0 || hour > 23
332             || (mday = tw->tw_mday) < 1 || mday > 31
333             || (mon = tw->tw_mon + 1) < 1 || mon > 12)
334         return (tw->tw_clock = (time_t) -1);
335
336     year = tw->tw_year;
337
338     result = 0;
339     if (year < 1970)
340       year += 1900;
341
342     if (year < 1970)
343       year += 100;
344
345     for (i = 1970; i < year; i++)
346         result += dysize (i);
347     if (dysize (year) == 366 && mon >= 3)
348         result++;
349     while (--mon)
350         result += dmsize[mon - 1];
351     result += mday - 1;
352     result = 24 * result + hour;
353     result = 60 * result + min;
354     result = 60 * result + sec;
355     result -= 60 * tw->tw_zone;
356     if (tw->tw_flags & TW_DST)
357         result -= 60 * 60;
358
359     return (tw->tw_clock = result);
360 }
361
362
363 /*
364  * Simple calculation of day of the week.  Algorithm
365  * used is Zeller's congruence.  We assume that
366  * if tw->tw_year < 100, then the century = 19.
367  */
368
369 void
370 set_dotw (struct tws *tw)
371 {
372     int month, day, year, century;
373
374     month = tw->tw_mon - 1;
375     day = tw->tw_mday;
376     year = tw->tw_year % 100;
377     century = tw->tw_year < 100 ? 19 : tw->tw_year / 100;
378
379     if (month <= 0) {
380         month += 12;
381         if (--year < 0) {
382             year += 100;
383             century--;
384         }
385     }
386
387     tw->tw_wday =
388         ((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 #if 0
407     tb->tw_sec   = tw->tw_sec;
408     tb->tw_min   = tw->tw_min;
409     tb->tw_hour  = tw->tw_hour;
410     tb->tw_mday  = tw->tw_mday;
411     tb->tw_mon   = tw->tw_mon;
412     tb->tw_year  = tw->tw_year;
413     tb->tw_wday  = tw->tw_wday;
414     tb->tw_yday  = tw->tw_yday;
415     tb->tw_zone  = tw->tw_zone;
416     tb->tw_clock = tw->tw_clock;
417     tb->tw_flags = tw->tw_flags;
418 #endif
419 }
420
421
422 /*
423  * Compare two nmh time structures
424  */
425
426 int
427 twsort (struct tws *tw1, struct tws *tw2)
428 {
429     time_t c1, c2;
430
431     if (tw1->tw_clock == 0)
432         dmktime (tw1);
433     if (tw2->tw_clock == 0)
434         dmktime (tw2);
435
436     return ((c1 = tw1->tw_clock) > (c2 = tw2->tw_clock) ? 1
437             : c1 == c2 ? 0 : -1);
438 }