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