upstream nginx-0.7.31
[nginx.git] / nginx / src / core / ngx_times.c
1
2 /*
3  * Copyright (C) Igor Sysoev
4  */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9
10
11 /*
12  * The time may be updated by signal handler or by several threads.
13  * The time update operations are rare and require to hold the ngx_time_lock.
14  * The time read operations are frequent, so they are lock-free and get time
15  * values and strings from the current slot.  Thus thread may get the corrupted
16  * values only if it is preempted while copying and then it is not scheduled
17  * to run more than NGX_TIME_SLOTS seconds.
18  */
19
20 #define NGX_TIME_SLOTS   64
21
22 static ngx_uint_t        slot;
23 static ngx_atomic_t      ngx_time_lock;
24
25 volatile ngx_msec_t      ngx_current_msec;
26 volatile ngx_time_t     *ngx_cached_time;
27 volatile ngx_str_t       ngx_cached_err_log_time;
28 volatile ngx_str_t       ngx_cached_http_time;
29 volatile ngx_str_t       ngx_cached_http_log_time;
30
31 static ngx_time_t        cached_time[NGX_TIME_SLOTS];
32 static u_char            cached_err_log_time[NGX_TIME_SLOTS]
33                                     [sizeof("1970/09/28 12:00:00")];
34 static u_char            cached_http_time[NGX_TIME_SLOTS]
35                                     [sizeof("Mon, 28 Sep 1970 06:00:00 GMT")];
36 static u_char            cached_http_log_time[NGX_TIME_SLOTS]
37                                     [sizeof("28/Sep/1970:12:00:00 +0600")];
38
39
40 static char  *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
41 static char  *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
42                            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
43
44 void
45 ngx_time_init(void)
46 {
47     ngx_cached_err_log_time.len = sizeof("1970/09/28 12:00:00") - 1;
48     ngx_cached_http_time.len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1;
49     ngx_cached_http_log_time.len = sizeof("28/Sep/1970:12:00:00 +0600") - 1;
50
51     ngx_cached_time = &cached_time[0];
52
53     ngx_time_update(0, 0);
54 }
55
56
57 void
58 ngx_time_update(time_t sec, ngx_uint_t msec)
59 {
60     u_char          *p0, *p1, *p2;
61     ngx_tm_t         tm, gmt;
62     ngx_time_t      *tp;
63     struct timeval   tv;
64
65     if (!ngx_trylock(&ngx_time_lock)) {
66         return;
67     }
68
69     if (sec == 0) {
70         ngx_gettimeofday(&tv);
71
72         sec = tv.tv_sec;
73         msec = tv.tv_usec / 1000;
74     }
75
76     ngx_current_msec = (ngx_msec_t) sec * 1000 + msec;
77
78     tp = &cached_time[slot];
79
80     if (tp->sec == sec) {
81         tp->msec = msec;
82         ngx_unlock(&ngx_time_lock);
83         return;
84     }
85
86     if (slot == NGX_TIME_SLOTS - 1) {
87         slot = 0;
88     } else {
89         slot++;
90     }
91
92     tp = &cached_time[slot];
93
94     tp->sec = sec;
95     tp->msec = msec;
96
97     ngx_gmtime(sec, &gmt);
98
99
100     p0 = &cached_http_time[slot][0];
101
102     (void) ngx_sprintf(p0, "%s, %02d %s %4d %02d:%02d:%02d GMT",
103                        week[gmt.ngx_tm_wday], gmt.ngx_tm_mday,
104                        months[gmt.ngx_tm_mon - 1], gmt.ngx_tm_year,
105                        gmt.ngx_tm_hour, gmt.ngx_tm_min, gmt.ngx_tm_sec);
106
107 #if (NGX_HAVE_GETTIMEZONE)
108
109     tp->gmtoff = ngx_gettimezone();
110     ngx_gmtime(sec + tp->gmtoff * 60, &tm);
111
112 #elif (NGX_HAVE_GMTOFF)
113
114     ngx_localtime(sec, &tm);
115     tp->gmtoff = (ngx_int_t) (tm.ngx_tm_gmtoff / 60);
116
117 #else
118
119     ngx_localtime(sec, &tm);
120     tp->gmtoff = ngx_timezone(tm.ngx_tm_isdst);
121
122 #endif
123
124
125     p1 = &cached_err_log_time[slot][0];
126
127     (void) ngx_sprintf(p1, "%4d/%02d/%02d %02d:%02d:%02d",
128                        tm.ngx_tm_year, tm.ngx_tm_mon,
129                        tm.ngx_tm_mday, tm.ngx_tm_hour,
130                        tm.ngx_tm_min, tm.ngx_tm_sec);
131
132
133     p2 = &cached_http_log_time[slot][0];
134
135     (void) ngx_sprintf(p2, "%02d/%s/%d:%02d:%02d:%02d %c%02d%02d",
136                        tm.ngx_tm_mday, months[tm.ngx_tm_mon - 1],
137                        tm.ngx_tm_year, tm.ngx_tm_hour,
138                        tm.ngx_tm_min, tm.ngx_tm_sec,
139                        tp->gmtoff < 0 ? '-' : '+',
140                        ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60));
141
142
143     ngx_memory_barrier();
144
145     ngx_cached_time = tp;
146     ngx_cached_http_time.data = p0;
147     ngx_cached_err_log_time.data = p1;
148     ngx_cached_http_log_time.data = p2;
149
150     ngx_unlock(&ngx_time_lock);
151 }
152
153
154 u_char *
155 ngx_http_time(u_char *buf, time_t t)
156 {
157     ngx_tm_t  tm;
158
159     ngx_gmtime(t, &tm);
160
161     return ngx_sprintf(buf, "%s, %02d %s %4d %02d:%02d:%02d GMT",
162                        week[tm.ngx_tm_wday],
163                        tm.ngx_tm_mday,
164                        months[tm.ngx_tm_mon - 1],
165                        tm.ngx_tm_year,
166                        tm.ngx_tm_hour,
167                        tm.ngx_tm_min,
168                        tm.ngx_tm_sec);
169 }
170
171
172 u_char *
173 ngx_http_cookie_time(u_char *buf, time_t t)
174 {
175     ngx_tm_t  tm;
176
177     ngx_gmtime(t, &tm);
178
179     /*
180      * Netscape 3.x does not understand 4-digit years at all and
181      * 2-digit years more than "37"
182      */
183
184     return ngx_sprintf(buf,
185                        (tm.ngx_tm_year > 2037) ?
186                                          "%s, %02d-%s-%d %02d:%02d:%02d GMT":
187                                          "%s, %02d-%s-%02d %02d:%02d:%02d GMT",
188                        week[tm.ngx_tm_wday],
189                        tm.ngx_tm_mday,
190                        months[tm.ngx_tm_mon - 1],
191                        (tm.ngx_tm_year > 2037) ? tm.ngx_tm_year:
192                                                  tm.ngx_tm_year % 100,
193                        tm.ngx_tm_hour,
194                        tm.ngx_tm_min,
195                        tm.ngx_tm_sec);
196 }
197
198
199 void
200 ngx_gmtime(time_t t, ngx_tm_t *tp)
201 {
202     ngx_int_t   yday;
203     ngx_uint_t  n, sec, min, hour, mday, mon, year, wday, days, leap;
204
205     /* the calculation is valid for positive time_t only */
206
207     n = (ngx_uint_t) t;
208
209     days = n / 86400;
210
211     /* Jaunary 1, 1970 was Thursday */
212
213     wday = (4 + days) % 7;
214
215     n %= 86400;
216     hour = n / 3600;
217     n %= 3600;
218     min = n / 60;
219     sec = n % 60;
220
221     /*
222      * the algorithm based on Gauss' formula,
223      * see src/http/ngx_http_parse_time.c
224      */
225
226     /* days since March 1, 1 BC */
227     days = days - (31 + 28) + 719527;
228
229     /*
230      * The "days" should be adjusted to 1 only, however, some March 1st's go
231      * to previous year, so we adjust them to 2.  This causes also shift of the
232      * last Feburary days to next year, but we catch the case when "yday"
233      * becomes negative.
234      */
235
236     year = (days + 2) * 400 / (365 * 400 + 100 - 4 + 1);
237
238     yday = days - (365 * year + year / 4 - year / 100 + year / 400);
239
240     if (yday < 0) {
241         leap = (year % 4 == 0) && (year % 100 || (year % 400 == 0));
242         yday = 365 + leap + yday;
243         year--;
244     }
245
246     /*
247      * The empirical formula that maps "yday" to month.
248      * There are at least 10 variants, some of them are:
249      *     mon = (yday + 31) * 15 / 459
250      *     mon = (yday + 31) * 17 / 520
251      *     mon = (yday + 31) * 20 / 612
252      */
253
254     mon = (yday + 31) * 10 / 306;
255
256     /* the Gauss' formula that evaluates days before the month */
257
258     mday = yday - (367 * mon / 12 - 30) + 1;
259
260     if (yday >= 306) {
261
262         year++;
263         mon -= 10;
264
265         /*
266          * there is no "yday" in Win32 SYSTEMTIME
267          *
268          * yday -= 306;
269          */
270
271     } else {
272
273         mon += 2;
274
275         /*
276          * there is no "yday" in Win32 SYSTEMTIME
277          *
278          * yday += 31 + 28 + leap;
279          */
280     }
281
282     tp->ngx_tm_sec = (ngx_tm_sec_t) sec;
283     tp->ngx_tm_min = (ngx_tm_min_t) min;
284     tp->ngx_tm_hour = (ngx_tm_hour_t) hour;
285     tp->ngx_tm_mday = (ngx_tm_mday_t) mday;
286     tp->ngx_tm_mon = (ngx_tm_mon_t) mon;
287     tp->ngx_tm_year = (ngx_tm_year_t) year;
288     tp->ngx_tm_wday = (ngx_tm_wday_t) wday;
289 }
290
291
292 time_t
293 ngx_next_time(time_t when)
294 {
295     time_t     now, next;
296     struct tm  tm;
297
298     now = ngx_time();
299
300     ngx_libc_localtime(now, &tm);
301
302     tm.tm_hour = (int) (when / 3600);
303     when %= 3600;
304     tm.tm_min = (int) (when / 60);
305     tm.tm_sec = (int) (when % 60);
306
307     next = mktime(&tm);
308
309     if (next == -1) {
310         return -1;
311     }
312
313     if (next - now > 0) {
314         return next;
315     }
316
317     tm.tm_mday++;
318
319     /* mktime() should normalize a date (Jan 32, etc) */
320
321     next = mktime(&tm);
322
323     if (next != -1) {
324         return next;
325     }
326
327     return -1;
328 }