Merge branch 'master' of git://git.exim.org/exim
[users/jgh/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 */