1 /*	$NetBSD: mail_date.c,v 1.1.1.1 2009/06/23 10:08:46 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	mail_date 3
6 /* SUMMARY
7 /*	return formatted time
8 /* SYNOPSIS
9 /*	#include <mail_date.h>
10 /*
11 /*	const char *mail_date(when)
12 /*	time_t	when;
13 /* DESCRIPTION
14 /*	mail_date() converts the time specified in \fIwhen\fR to the
15 /*	form: "Mon, 9 Dec 1996 05:38:26 -0500 (EST)" and returns
16 /*	a pointer to the result. The result is overwritten upon
17 /*	each call.
18 /* DIAGNOSTICS
19 /*	Panic: the offset from UTC is more than a whole day. Fatal
20 /*	error: out of memory.
21 /* LICENSE
22 /* .ad
23 /* .fi
24 /*	The Secure Mailer license must be distributed with this software.
25 /* AUTHOR(S)
26 /*	Wietse Venema
27 /*	IBM T.J. Watson Research
28 /*	P.O. Box 704
29 /*	Yorktown Heights, NY 10598, USA
30 /*--*/
31 
32 /* System library. */
33 
34 #include <sys_defs.h>
35 #include <time.h>
36 #include <stdlib.h>
37 
38 /* Utility library. */
39 
40 #include <msg.h>
41 #include <vstring.h>
42 
43 /* Global library. */
44 
45 #include "mail_date.h"
46 
47  /*
48   * Application-specific.
49   */
50 #define DAY_MIN		(24 * HOUR_MIN)	/* minutes in a day */
51 #define	HOUR_MIN	60		/* minutes in an hour */
52 #define MIN_SEC		60		/* seconds in a minute */
53 
54 /* mail_date - return formatted time */
55 
56 const char *mail_date(time_t when)
57 {
58     static VSTRING *vp;
59     struct tm *lt;
60     struct tm gmt;
61     int     gmtoff;
62 
63     /*
64      * As if strftime() isn't expensive enough, we're dynamically adjusting
65      * the size for the result, so we won't be surprised by long names etc.
66      */
67     if (vp == 0)
68 	vp = vstring_alloc(100);
69     else
70 	VSTRING_RESET(vp);
71 
72     /*
73      * POSIX does not require that struct tm has a tm_gmtoff field, so we
74      * must compute the time offset from UTC by hand.
75      *
76      * Starting with the difference in hours/minutes between 24-hour clocks,
77      * adjust for differences in years, in yeardays, and in (leap) seconds.
78      *
79      * Assume 0..23 hours in a day, 0..59 minutes in an hour. The library spec
80      * has changed: we can no longer assume that there are 0..59 seconds in a
81      * minute.
82      */
83     gmt = *gmtime(&when);
84     lt = localtime(&when);
85     gmtoff = (lt->tm_hour - gmt.tm_hour) * HOUR_MIN + lt->tm_min - gmt.tm_min;
86     if (lt->tm_year < gmt.tm_year)
87 	gmtoff -= DAY_MIN;
88     else if (lt->tm_year > gmt.tm_year)
89 	gmtoff += DAY_MIN;
90     else if (lt->tm_yday < gmt.tm_yday)
91 	gmtoff -= DAY_MIN;
92     else if (lt->tm_yday > gmt.tm_yday)
93 	gmtoff += DAY_MIN;
94     if (lt->tm_sec <= gmt.tm_sec - MIN_SEC)
95 	gmtoff -= 1;
96     else if (lt->tm_sec >= gmt.tm_sec + MIN_SEC)
97 	gmtoff += 1;
98 
99     /*
100      * First, format the date and wall-clock time. XXX The %e format (day of
101      * month, leading zero replaced by blank) isn't in my POSIX book, but
102      * many vendors seem to support it.
103      */
104 #ifdef MISSING_STRFTIME_E
105 #define STRFTIME_FMT "%a, %d %b %Y %H:%M:%S "
106 #else
107 #define STRFTIME_FMT "%a, %e %b %Y %H:%M:%S "
108 #endif
109 
110     while (strftime(vstring_end(vp), vstring_avail(vp), STRFTIME_FMT, lt) == 0)
111 	VSTRING_SPACE(vp, 100);
112     VSTRING_SKIP(vp);
113 
114     /*
115      * Then, add the UTC offset.
116      */
117     if (gmtoff < -DAY_MIN || gmtoff > DAY_MIN)
118 	msg_panic("UTC time offset %d is larger than one day", gmtoff);
119     vstring_sprintf_append(vp, "%+03d%02d", (int) (gmtoff / HOUR_MIN),
120 			   (int) (abs(gmtoff) % HOUR_MIN));
121 
122     /*
123      * Finally, add the time zone name.
124      */
125     while (strftime(vstring_end(vp), vstring_avail(vp), " (%Z)", lt) == 0)
126 	VSTRING_SPACE(vp, vstring_avail(vp) + 100);
127     VSTRING_SKIP(vp);
128 
129     return (vstring_str(vp));
130 }
131 
132 #ifdef TEST
133 
134 #include <vstream.h>
135 
136 int     main(void)
137 {
138     vstream_printf("%s\n", mail_date(time((time_t *) 0)));
139     vstream_fflush(VSTREAM_OUT);
140     return (0);
141 }
142 
143 #endif
144