Commit | Line | Data |
---|---|---|
059ec3d9 PH |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
4 | ||
f9ba5e22 | 5 | /* Copyright (c) University of Cambridge 1995 - 2018 */ |
1e1ddfac | 6 | /* Copyright (c) The Exim Maintainers 2020 */ |
059ec3d9 PH |
7 | /* See the file NOTICE for conditions of use and distribution. */ |
8 | ||
9 | /* A function for returning the time of day in various formats */ | |
10 | ||
11 | ||
12 | #include "exim.h" | |
13 | ||
14 | /* #define TESTING_LOG_DATESTAMP */ | |
15 | ||
16 | ||
571b2715 | 17 | static uschar timebuf[sizeof("www, dd-mmm-yyyy hh:mm:ss.ddd +zzzz")]; |
059ec3d9 PH |
18 | |
19 | ||
20 | /************************************************* | |
21 | * Return timestamp * | |
22 | *************************************************/ | |
23 | ||
24 | /* The log timestamp format is dd-mmm-yy so as to be non-confusing on both | |
25 | sides of the Atlantic. We calculate an explicit numerical offset from GMT for | |
26 | the full datestamp and BSD inbox datestamp. Note that on some systems | |
27 | localtime() and gmtime() re-use the same store, so we must save the local time | |
28 | values before calling gmtime(). If timestamps_utc is set, don't use | |
29 | localtime(); all times are then in UTC (with offset +0000). | |
30 | ||
31 | There are also some contortions to get the day of the month without | |
32 | a leading zero for the full stamp, since Ustrftime() doesn't provide this | |
33 | option. | |
34 | ||
35 | Argument: type of timestamp required: | |
f1e5fef5 PP |
36 | tod_bsdin BSD inbox format |
37 | tod_epoch Unix epoch format | |
f5787926 | 38 | tod_epochl Unix epoch/usec format |
f1e5fef5 PP |
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 | |
059ec3d9 PH |
49 | |
50 | Returns: pointer to fixed buffer containing the timestamp | |
51 | */ | |
52 | ||
53 | uschar * | |
54 | tod_stamp(int type) | |
55 | { | |
571b2715 JH |
56 | struct timeval now; |
57 | struct tm * t; | |
059ec3d9 | 58 | |
571b2715 | 59 | gettimeofday(&now, NULL); |
059ec3d9 PH |
60 | |
61 | /* Styles that don't need local time */ | |
62 | ||
571b2715 | 63 | switch(type) |
059ec3d9 | 64 | { |
571b2715 | 65 | case tod_epoch: |
b09c5f34 | 66 | (void) snprintf(CS timebuf, sizeof(timebuf), TIME_T_FMT, now.tv_sec); /* Unix epoch format */ |
571b2715 JH |
67 | return timebuf; /* NB the above will be wrong if time_t is FP */ |
68 | ||
69 | case tod_epoch_l: | |
70 | /* Unix epoch/usec format */ | |
b09c5f34 | 71 | (void) snprintf(CS timebuf, sizeof(timebuf), TIME_T_FMT "%06ld", now.tv_sec, (long) now.tv_usec ); |
571b2715 JH |
72 | return timebuf; |
73 | ||
74 | case tod_zulu: | |
75 | t = gmtime(&now.tv_sec); | |
b09c5f34 | 76 | (void) snprintf(CS timebuf, sizeof(timebuf), "%04u%02u%02u%02u%02u%02uZ", |
13c7874e JH |
77 | 1900 + (uint)t->tm_year, 1 + (uint)t->tm_mon, (uint)t->tm_mday, (uint)t->tm_hour, (uint)t->tm_min, |
78 | (uint)t->tm_sec); | |
571b2715 | 79 | return timebuf; |
059ec3d9 PH |
80 | } |
81 | ||
571b2715 JH |
82 | /* Vary log type according to timezone requirement */ |
83 | ||
84 | if (type == tod_log) type = log_timezone ? tod_log_zone : tod_log_bare; | |
059ec3d9 PH |
85 | |
86 | /* Convert to local time or UTC */ | |
87 | ||
8768d548 | 88 | t = f.timestamps_utc ? gmtime(&now.tv_sec) : localtime(&now.tv_sec); |
059ec3d9 PH |
89 | |
90 | switch(type) | |
91 | { | |
92 | case tod_log_bare: /* Format used in logging without timezone */ | |
753b36c5 HSHR |
93 | #ifndef COMPILE_UTILITY |
94 | if (LOGGING(millisec)) | |
b09c5f34 | 95 | snprintf(CS timebuf, sizeof(timebuf), "%04u-%02u-%02u %02u:%02u:%02u.%03u", |
13c7874e JH |
96 | 1900 + (uint)t->tm_year, 1 + (uint)t->tm_mon, (uint)t->tm_mday, |
97 | (uint)t->tm_hour, (uint)t->tm_min, (uint)t->tm_sec, | |
98 | (uint)(now.tv_usec/1000)); | |
753b36c5 HSHR |
99 | else |
100 | #endif | |
b09c5f34 | 101 | snprintf(CS timebuf, sizeof(timebuf), "%04u-%02u-%02u %02u:%02u:%02u", |
13c7874e JH |
102 | 1900 + (uint)t->tm_year, 1 + (uint)t->tm_mon, (uint)t->tm_mday, |
103 | (uint)t->tm_hour, (uint)t->tm_min, (uint)t->tm_sec); | |
753b36c5 | 104 | |
571b2715 | 105 | break; |
059ec3d9 | 106 | |
571b2715 JH |
107 | /* Format used as suffix of log file name when 'log_datestamp' is active. For |
108 | testing purposes, it changes the file every second. */ | |
059ec3d9 | 109 | |
571b2715 | 110 | #ifdef TESTING_LOG_DATESTAMP |
f1e5fef5 PP |
111 | case tod_log_datestamp_daily: |
112 | case tod_log_datestamp_monthly: | |
b09c5f34 | 113 | snprintf(CS timebuf, sizeof(timebuf), "%04u%02u%02u%02u%02u", |
13c7874e JH |
114 | 1900 + (uint)t->tm_year, 1 + (uint)t->tm_mon, (uint)t->tm_mday, |
115 | (uint)t->tm_hour, (uint)t->tm_min); | |
571b2715 | 116 | break; |
f1e5fef5 | 117 | |
571b2715 | 118 | #else |
f1e5fef5 | 119 | case tod_log_datestamp_daily: |
b09c5f34 | 120 | snprintf(CS timebuf, sizeof(timebuf), "%04u%02u%02u", |
13c7874e | 121 | 1900 + (uint)t->tm_year, 1 + (uint)t->tm_mon, (uint)t->tm_mday); |
571b2715 | 122 | break; |
059ec3d9 | 123 | |
f1e5fef5 | 124 | case tod_log_datestamp_monthly: |
cab0c277 | 125 | #ifndef COMPILE_UTILITY |
b09c5f34 | 126 | snprintf(CS timebuf, sizeof(timebuf), "%04u%02u", |
13c7874e | 127 | 1900 + (uint)t->tm_year, 1 + (uint)t->tm_mon); |
cab0c277 | 128 | #endif |
571b2715 JH |
129 | break; |
130 | #endif | |
f1e5fef5 | 131 | |
571b2715 JH |
132 | /* Format used in BSD inbox separator lines. Sort-of documented in RFC 976 |
133 | ("UUCP Mail Interchange Format Standard") but only by example, not by | |
134 | explicit definition. The examples show no timezone offsets, and some MUAs | |
135 | appear to be sensitive to this, so Exim has been changed to remove the | |
136 | timezone offsets that originally appeared. */ | |
059ec3d9 PH |
137 | |
138 | case tod_bsdin: | |
059ec3d9 | 139 | { |
571b2715 JH |
140 | int len = Ustrftime(timebuf, sizeof(timebuf), "%a %b %d %H:%M:%S", t); |
141 | Ustrftime(timebuf + len, sizeof(timebuf) - len, " %Y", t); | |
059ec3d9 | 142 | } |
571b2715 JH |
143 | break; |
144 | ||
145 | /* Other types require the GMT offset to be calculated, or just set up in the | |
146 | case of UTC timestamping. We need to take a copy of the local time first. */ | |
059ec3d9 | 147 | |
571b2715 | 148 | default: |
059ec3d9 | 149 | { |
571b2715 JH |
150 | int diff_hour, diff_min; |
151 | struct tm local; | |
158d7137 AW |
152 | struct tm * lp = &local; |
153 | memcpy(lp, t, sizeof(struct tm)); | |
571b2715 | 154 | |
8768d548 | 155 | if (f.timestamps_utc) |
571b2715 JH |
156 | diff_hour = diff_min = 0; |
157 | else | |
158 | { | |
159 | struct tm * gmt = gmtime(&now.tv_sec); | |
160 | ||
158d7137 AW |
161 | if (local.tm_sec == gmt->tm_sec) /* usual case */ |
162 | { | |
163 | diff_min = 60 * (local.tm_hour - gmt->tm_hour) | |
164 | + local.tm_min - gmt->tm_min; | |
165 | if (local.tm_year != gmt->tm_year) | |
166 | diff_min += (local.tm_year > gmt->tm_year) ? 1440 : -1440; | |
167 | else if (local.tm_yday != gmt->tm_yday) | |
168 | diff_min += (local.tm_yday > gmt->tm_yday) ? 1440 : -1440; | |
169 | diff_hour = diff_min/60; | |
170 | diff_min = abs(diff_min - diff_hour*60); | |
171 | } | |
172 | else /* subminute offset, eg. TAI */ | |
173 | { | |
174 | lp = gmt; /* pretend we're in UTC */ | |
175 | diff_min = diff_hour = 0; | |
176 | } | |
571b2715 JH |
177 | } |
178 | ||
179 | switch(type) | |
180 | { | |
181 | case tod_log_zone: /* Format used in logging with timezone */ | |
182 | #ifndef COMPILE_UTILITY | |
183 | if (LOGGING(millisec)) | |
b09c5f34 | 184 | (void) snprintf(CS timebuf, sizeof(timebuf), |
13c7874e | 185 | "%04u-%02u-%02u %02u:%02u:%02u.%03u %+03d%02d", |
158d7137 AW |
186 | 1900 + (uint)lp->tm_year, 1 + (uint)lp->tm_mon, (uint)lp->tm_mday, |
187 | (uint)lp->tm_hour, (uint)lp->tm_min, (uint)lp->tm_sec, (uint)(now.tv_usec/1000), | |
571b2715 JH |
188 | diff_hour, diff_min); |
189 | else | |
190 | #endif | |
b09c5f34 | 191 | (void) snprintf(CS timebuf, sizeof(timebuf), |
13c7874e | 192 | "%04u-%02u-%02u %02u:%02u:%02u %+03d%02d", |
158d7137 AW |
193 | 1900 + (uint)lp->tm_year, 1 + (uint)lp->tm_mon, (uint)lp->tm_mday, |
194 | (uint)lp->tm_hour, (uint)lp->tm_min, (uint)lp->tm_sec, | |
571b2715 JH |
195 | diff_hour, diff_min); |
196 | break; | |
197 | ||
198 | case tod_zone: /* Just the timezone offset */ | |
b09c5f34 | 199 | (void) snprintf(CS timebuf, sizeof(timebuf), "%+03d%02d", diff_hour, diff_min); |
571b2715 JH |
200 | break; |
201 | ||
202 | /* tod_mbx: format used in MBX mailboxes - subtly different to tod_full */ | |
203 | ||
158d7137 | 204 | #ifdef SUPPORT_MBX |
571b2715 | 205 | case tod_mbx: |
158d7137 AW |
206 | { |
207 | int len = snprintf(CS timebuf, sizeof(timebuf), | |
208 | "%02u-", (uint)lp->tm_mday); | |
209 | len += Ustrftime(timebuf + len, sizeof(timebuf) - len, | |
210 | "%b-%Y %H:%M:%S", lp); | |
211 | (void) snprintf(CS timebuf + len, sizeof(timebuf)-len, | |
212 | " %+03d%02d", diff_hour, diff_min); | |
213 | } | |
571b2715 | 214 | break; |
158d7137 | 215 | #endif |
571b2715 JH |
216 | |
217 | /* tod_full: format used in Received: headers (use as default just in case | |
218 | called with a junk type value) */ | |
219 | ||
220 | default: | |
158d7137 AW |
221 | { |
222 | int len = Ustrftime(timebuf, sizeof(timebuf), "%a, ", lp); | |
223 | len += snprintf(CS timebuf + len, sizeof(timebuf)-len, | |
224 | "%02u ", (uint)lp->tm_mday); | |
225 | len += Ustrftime(timebuf + len, sizeof(timebuf) - len, | |
226 | "%b %Y %H:%M:%S", lp); | |
227 | (void) snprintf(CS timebuf + len, sizeof(timebuf)-len, | |
228 | " %+03d%02d", diff_hour, diff_min); | |
229 | } | |
571b2715 JH |
230 | break; |
231 | } | |
059ec3d9 | 232 | } |
571b2715 | 233 | break; |
059ec3d9 PH |
234 | } |
235 | ||
236 | return timebuf; | |
237 | } | |
238 | ||
239 | /* End of tod.c */ |