Home | History | Annotate | Line # | Download | only in global
      1 /*	$NetBSD: mail_date.c,v 1.2 2025/02/25 19:15:45 christos 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, 09 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      * The RFC 5322 Date and Time Specification recommends (i.e., should) "that
    105      * a single space be used in each place that FWS appears". To avoid a
    106      * potentially breaking change, we prefer the %d (two-digit day) format,
    107      * i.e. days 1-9 now have a leading zero instead of a leading space.
    108      */
    109 #if defined(MISSING_STRFTIME_E) || defined(TWO_DIGIT_DAY_IN_DATE_TIME)
    110 #define STRFTIME_FMT "%a, %d %b %Y %H:%M:%S "
    111 #else
    112 #define STRFTIME_FMT "%a, %e %b %Y %H:%M:%S "
    113 #endif
    114 
    115     while (strftime(vstring_end(vp), vstring_avail(vp), STRFTIME_FMT, lt) == 0)
    116 	VSTRING_SPACE(vp, 100);
    117     VSTRING_SKIP(vp);
    118 
    119     /*
    120      * Then, add the UTC offset.
    121      */
    122     if (gmtoff < -DAY_MIN || gmtoff > DAY_MIN)
    123 	msg_panic("UTC time offset %d is larger than one day", gmtoff);
    124     vstring_sprintf_append(vp, "%+03d%02d", (int) (gmtoff / HOUR_MIN),
    125 			   (int) (abs(gmtoff) % HOUR_MIN));
    126 
    127     /*
    128      * Finally, add the time zone name.
    129      */
    130     while (strftime(vstring_end(vp), vstring_avail(vp), " (%Z)", lt) == 0)
    131 	VSTRING_SPACE(vp, vstring_avail(vp) + 100);
    132     VSTRING_SKIP(vp);
    133 
    134     return (vstring_str(vp));
    135 }
    136 
    137 #ifdef TEST
    138 
    139 #include <vstream.h>
    140 
    141 int     main(void)
    142 {
    143     vstream_printf("%s\n", mail_date(time((time_t *) 0)));
    144     vstream_fflush(VSTREAM_OUT);
    145     return (0);
    146 }
    147 
    148 #endif
    149