Implement %M datestamping in log filenames.
[exim.git] / src / src / tod.c
1 /* $Cambridge: exim/src/src/tod.c,v 1.5 2009/11/16 19:50:37 nm4 Exp $ */
2
3 /*************************************************
4 *     Exim - an Internet mail transport agent    *
5 *************************************************/
6
7 /* Copyright (c) University of Cambridge 1995 - 2009 */
8 /* See the file NOTICE for conditions of use and distribution. */
9
10 /* A function for returning the time of day in various formats */
11
12
13 #include "exim.h"
14
15 /* #define TESTING_LOG_DATESTAMP */
16
17
18 static uschar timebuf[sizeof("www, dd-mmm-yyyy hh:mm:ss +zzzz")];
19
20
21 /*************************************************
22 *                Return timestamp                *
23 *************************************************/
24
25 /* The log timestamp format is dd-mmm-yy so as to be non-confusing on both
26 sides of the Atlantic. We calculate an explicit numerical offset from GMT for
27 the full datestamp and BSD inbox datestamp. Note that on some systems
28 localtime() and gmtime() re-use the same store, so we must save the local time
29 values before calling gmtime(). If timestamps_utc is set, don't use
30 localtime(); all times are then in UTC (with offset +0000).
31
32 There are also some contortions to get the day of the month without
33 a leading zero for the full stamp, since Ustrftime() doesn't provide this
34 option.
35
36 Argument:  type of timestamp required:
37              tod_bsdin                  BSD inbox format
38              tod_epoch                  Unix epoch format
39              tod_full                   full date and time
40              tod_log                    log file data line format,
41                                           with zone if log_timezone is TRUE
42              tod_log_bare               always without zone
43              tod_log_datestamp_daily    for log file names when datestamped daily
44              tod_log_datestamp_monthly  for log file names when datestamped monthly
45              tod_log_zone               always with zone
46              tod_mbx                    MBX inbox format
47              tod_zone                   just the timezone offset
48              tod_zulu                   time in 8601 zulu format
49
50 Returns:   pointer to fixed buffer containing the timestamp
51 */
52
53 uschar *
54 tod_stamp(int type)
55 {
56 time_t now = time(NULL);
57 struct tm *t;
58
59 /* Vary log type according to timezone requirement */
60
61 if (type == tod_log) type = log_timezone? tod_log_zone : tod_log_bare;
62
63 /* Styles that don't need local time */
64
65 else if (type == tod_epoch)
66   {
67   (void) sprintf(CS timebuf, "%d", (int)now);  /* Unix epoch format */
68   return timebuf;
69   }
70
71 else if (type == tod_zulu)
72   {
73   t = gmtime(&now);
74   (void) sprintf(CS timebuf, "%04d%02d%02d%02d%02d%02dZ",
75     1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min,
76     t->tm_sec);
77   return timebuf;
78   }
79
80 /* Convert to local time or UTC */
81
82 t = timestamps_utc? gmtime(&now) : localtime(&now);
83
84 switch(type)
85   {
86   case tod_log_bare:          /* Format used in logging without timezone */
87   (void) sprintf(CS timebuf, "%04d-%02d-%02d %02d:%02d:%02d",
88     1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday,
89     t->tm_hour, t->tm_min, t->tm_sec);
90   break;
91
92   /* Format used as suffix of log file name when 'log_datestamp' is active. For
93   testing purposes, it changes the file every second. */
94
95   #ifdef TESTING_LOG_DATESTAMP
96   case tod_log_datestamp_daily:
97   case tod_log_datestamp_monthly:
98   (void) sprintf(CS timebuf, "%04d%02d%02d%02d%02d",
99     1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min);
100   break;
101
102   #else
103   case tod_log_datestamp_daily:
104   (void) sprintf(CS timebuf, "%04d%02d%02d",
105     1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday);
106   break;
107
108   case tod_log_datestamp_monthly:
109   (void) sprintf(CS timebuf, "%04d%02d",
110     1900 + t->tm_year, 1 + t->tm_mon);
111   break;
112   #endif
113
114   /* Format used in BSD inbox separator lines. Sort-of documented in RFC 976
115   ("UUCP Mail Interchange Format Standard") but only by example, not by
116   explicit definition. The examples show no timezone offsets, and some MUAs
117   appear to be sensitive to this, so Exim has been changed to remove the
118   timezone offsets that originally appeared. */
119
120   case tod_bsdin:
121     {
122     int len = Ustrftime(timebuf, sizeof(timebuf), "%a %b %d %H:%M:%S", t);
123     Ustrftime(timebuf + len, sizeof(timebuf) - len, " %Y", t);
124     }
125   break;
126
127   /* Other types require the GMT offset to be calculated, or just set up in the
128   case of UTC timestamping. We need to take a copy of the local time first. */
129
130   default:
131     {
132     int diff_hour, diff_min;
133     struct tm local;
134     memcpy(&local, t, sizeof(struct tm));
135
136     if (timestamps_utc)
137       {
138       diff_hour = diff_min = 0;
139       }
140     else
141       {
142       struct tm *gmt = gmtime(&now);
143       diff_min = 60*(local.tm_hour - gmt->tm_hour) + local.tm_min - gmt->tm_min;
144       if (local.tm_year != gmt->tm_year)
145         diff_min += (local.tm_year > gmt->tm_year)? 1440 : -1440;
146       else if (local.tm_yday != gmt->tm_yday)
147         diff_min += (local.tm_yday > gmt->tm_yday)? 1440 : -1440;
148       diff_hour = diff_min/60;
149       diff_min  = abs(diff_min - diff_hour*60);
150       }
151
152     switch(type)
153       {
154       case tod_log_zone:          /* Format used in logging with timezone */
155       (void) sprintf(CS timebuf, "%04d-%02d-%02d %02d:%02d:%02d %+03d%02d",
156         1900 + local.tm_year, 1 + local.tm_mon, local.tm_mday,
157         local.tm_hour, local.tm_min, local.tm_sec,
158         diff_hour, diff_min);
159       break;
160
161       case tod_zone:              /* Just the timezone offset */
162       (void) sprintf(CS timebuf, "%+03d%02d", diff_hour, diff_min);
163       break;
164
165       /* tod_mbx: format used in MBX mailboxes - subtly different to tod_full */
166
167       #ifdef SUPPORT_MBX
168       case tod_mbx:
169         {
170         int len;
171         (void) sprintf(CS timebuf, "%02d-", local.tm_mday);
172         len = Ustrlen(timebuf);
173         len += Ustrftime(timebuf + len, sizeof(timebuf) - len, "%b-%Y %H:%M:%S",
174           &local);
175         (void) sprintf(CS timebuf + len, " %+03d%02d", diff_hour, diff_min);
176         }
177       break;
178       #endif
179
180       /* tod_full: format used in Received: headers (use as default just in case
181       called with a junk type value) */
182
183       default:
184         {
185         int len = Ustrftime(timebuf, sizeof(timebuf), "%a, ", &local);
186         (void) sprintf(CS timebuf + len, "%02d ", local.tm_mday);
187         len += Ustrlen(timebuf + len);
188         len += Ustrftime(timebuf + len, sizeof(timebuf) - len, "%b %Y %H:%M:%S",
189           &local);
190         (void) sprintf(CS timebuf + len, " %+03d%02d", diff_hour, diff_min);
191         }
192       break;
193       }
194     }
195   break;
196   }
197
198 return timebuf;
199 }
200
201 /* End of tod.c */