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