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