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