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