We have only numeric timezone support (e.g. +0200) from now on.
[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 -0700
201 **
202 ** for the current time, as specified by rfc822 and rfc1123.
203 */
204 char *
205 dtimenow(void)
206 {
207         time_t clock;
208
209         time(&clock);
210         return dtime(&clock);
211 }
212
213
214 /*
215 ** Using a local calendar time value, produce
216 ** a date/time string of the form
217 **
218 **    Mon, 16 Jun 1992 15:30:48 -0700
219 **
220 ** as specified by rfc822 and rfc1123.
221 */
222 char *
223 dtime(time_t *clock)
224 {
225         return dasctime(dlocaltime(clock));
226 }
227
228
229 /*
230 ** Using a nmh "broken-down" time structure, produce
231 ** a date/time string of the form
232 **
233 **    Mon, 16 Jun 1992 15:30:48 -0700
234 **
235 ** as specified by rfc822 and rfc1123.
236 */
237 char *
238 dasctime(struct tws *tw)
239 {
240         char buffer[80];
241         static char result[80];
242
243         if (!tw)
244                 return NULL;
245
246         /* Display timezone if known */
247         if ((tw->tw_flags & TW_SZONE) == TW_SZNIL)
248                 result[0] = '\0';
249         else
250                 snprintf(result, sizeof(result), " %s", dtimezone(tw->tw_zone, tw->tw_flags));
251
252         snprintf(buffer, sizeof(buffer), "%02d %s %0*d %02d:%02d:%02d%s",
253                 tw->tw_mday, tw_moty[tw->tw_mon],
254                 tw->tw_year < 100 ? 2 : 4, tw->tw_year,
255                 tw->tw_hour, tw->tw_min, tw->tw_sec, result);
256
257         if ((tw->tw_flags & TW_SDAY) == TW_SEXP)
258                 snprintf(result, sizeof(result), "%s, %s", tw_dotw[tw->tw_wday], buffer);
259         else
260                 if ((tw->tw_flags & TW_SDAY) == TW_SNIL)
261                         strncpy(result, buffer, sizeof(result));
262                 else
263                         snprintf(result, sizeof(result), "%s (%s)", buffer, tw_dotw[tw->tw_wday]);
264
265         return result;
266 }
267
268
269 /*
270 ** Get the timezone for given offset as numeric value.
271 */
272 char *
273 dtimezone(int offset, int flags)
274 {
275         int hours, mins;
276         static char buffer[10];
277
278         if (offset < 0) {
279                 mins = -((-offset) % 60);
280                 hours = -((-offset) / 60);
281         } else {
282                 mins = offset % 60;
283                 hours = offset / 60;
284         }
285
286 #ifdef ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST
287         if (flags & TW_DST)
288                 hours += 1;
289 #endif /* ADJUST_NUMERIC_ONLY_TZ_OFFSETS_WRT_DST */
290         snprintf(buffer, sizeof(buffer), "%s%02d%02d",
291                 offset < 0 ? "-" : "+", abs(hours), abs(mins));
292         return buffer;
293 }
294
295
296 /*
297 ** Convert nmh time structure for local "broken-down"
298 ** time to calendar time (clock value).  This routine
299 ** is based on the gtime() routine written by Steven Shafer
300 ** at CMU.  It was forwarded to MTR by Jay Lepreau at Utah-CS.
301 */
302
303 time_t
304 dmktime(struct tws *tw)
305 {
306         int i, sec, min, hour, mday, mon, year;
307         time_t result;
308
309         if (tw->tw_clock != 0)
310                 return tw->tw_clock;
311
312         if ((sec = tw->tw_sec) < 0 || sec > 61
313                 || (min = tw->tw_min) < 0 || min > 59
314                 || (hour = tw->tw_hour) < 0 || hour > 23
315                 || (mday = tw->tw_mday) < 1 || mday > 31
316                 || (mon = tw->tw_mon + 1) < 1 || mon > 12)
317                 return (tw->tw_clock = (time_t) -1);
318
319         year = tw->tw_year;
320
321         result = 0;
322         if (year < 1970)
323                 year += 1900;
324
325         if (year < 1970)
326                 year += 100;
327
328         for (i = 1970; i < year; i++)
329                 result += dysize(i);
330         if (dysize(year) == 366 && mon >= 3)
331                 result++;
332         while (--mon)
333                 result += dmsize[mon - 1];
334         result += mday - 1;
335         result = 24 * result + hour;
336         result = 60 * result + min;
337         result = 60 * result + sec;
338         result -= 60 * tw->tw_zone;
339         if (tw->tw_flags & TW_DST)
340                 result -= 60 * 60;
341
342         return (tw->tw_clock = result);
343 }
344
345
346 /*
347 ** Simple calculation of day of the week.  Algorithm
348 ** used is Zeller's congruence.  We assume that
349 ** if tw->tw_year < 100, then the century = 19.
350 */
351
352 void
353 set_dotw(struct tws *tw)
354 {
355         int month, day, year, century;
356
357         month = tw->tw_mon - 1;
358         day = tw->tw_mday;
359         year = tw->tw_year % 100;
360         century = tw->tw_year < 100 ? 19 : tw->tw_year / 100;
361
362         if (month <= 0) {
363                 month += 12;
364                 if (--year < 0) {
365                         year += 100;
366                         century--;
367                 }
368         }
369
370         tw->tw_wday = ((26 * month - 2) / 10 + day + year + year / 4
371                         - 3 * century / 4 + 1) % 7;
372         if (tw->tw_wday < 0)
373                 tw->tw_wday += 7;
374
375         tw->tw_flags &= ~TW_SDAY, tw->tw_flags |= TW_SIMP;
376 }
377
378
379 /*
380 ** Copy nmh time structure
381 */
382
383 void
384 twscopy(struct tws *tb, struct tws *tw)
385 {
386         *tb = *tw;  /* struct copy */
387 }
388
389
390 /*
391 ** Compare two nmh time structures
392 */
393
394 int
395 twsort(struct tws *tw1, struct tws *tw2)
396 {
397         time_t c1, c2;
398
399         if (tw1->tw_clock == 0)
400                 dmktime(tw1);
401         if (tw2->tw_clock == 0)
402                 dmktime(tw2);
403
404         return ((c1 = tw1->tw_clock) > (c2 = tw2->tw_clock) ? 1
405                 : c1 == c2 ? 0 : -1);
406 }