xref: /f-stack/app/nginx-1.16.1/src/core/ngx_times.c (revision 3da8d17d)
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 
11 
12 static ngx_msec_t ngx_monotonic_time(time_t sec, ngx_uint_t msec);
13 
14 
15 /*
16  * The time may be updated by signal handler or by several threads.
17  * The time update operations are rare and require to hold the ngx_time_lock.
18  * The time read operations are frequent, so they are lock-free and get time
19  * values and strings from the current slot.  Thus thread may get the corrupted
20  * values only if it is preempted while copying and then it is not scheduled
21  * to run more than NGX_TIME_SLOTS seconds.
22  */
23 
24 #define NGX_TIME_SLOTS   64
25 
26 static ngx_uint_t        slot;
27 static ngx_atomic_t      ngx_time_lock;
28 
29 volatile ngx_msec_t      ngx_current_msec;
30 volatile ngx_time_t     *ngx_cached_time;
31 volatile ngx_str_t       ngx_cached_err_log_time;
32 volatile ngx_str_t       ngx_cached_http_time;
33 volatile ngx_str_t       ngx_cached_http_log_time;
34 volatile ngx_str_t       ngx_cached_http_log_iso8601;
35 volatile ngx_str_t       ngx_cached_syslog_time;
36 
37 #if !(NGX_WIN32)
38 
39 /*
40  * localtime() and localtime_r() are not Async-Signal-Safe functions, therefore,
41  * they must not be called by a signal handler, so we use the cached
42  * GMT offset value. Fortunately the value is changed only two times a year.
43  */
44 
45 static ngx_int_t         cached_gmtoff;
46 #endif
47 
48 static ngx_time_t        cached_time[NGX_TIME_SLOTS];
49 static u_char            cached_err_log_time[NGX_TIME_SLOTS]
50                                     [sizeof("1970/09/28 12:00:00")];
51 static u_char            cached_http_time[NGX_TIME_SLOTS]
52                                     [sizeof("Mon, 28 Sep 1970 06:00:00 GMT")];
53 static u_char            cached_http_log_time[NGX_TIME_SLOTS]
54                                     [sizeof("28/Sep/1970:12:00:00 +0600")];
55 static u_char            cached_http_log_iso8601[NGX_TIME_SLOTS]
56                                     [sizeof("1970-09-28T12:00:00+06:00")];
57 static u_char            cached_syslog_time[NGX_TIME_SLOTS]
58                                     [sizeof("Sep 28 12:00:00")];
59 
60 
61 static char  *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
62 static char  *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
63                            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
64 
65 void
ngx_time_init(void)66 ngx_time_init(void)
67 {
68     ngx_cached_err_log_time.len = sizeof("1970/09/28 12:00:00") - 1;
69     ngx_cached_http_time.len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1;
70     ngx_cached_http_log_time.len = sizeof("28/Sep/1970:12:00:00 +0600") - 1;
71     ngx_cached_http_log_iso8601.len = sizeof("1970-09-28T12:00:00+06:00") - 1;
72     ngx_cached_syslog_time.len = sizeof("Sep 28 12:00:00") - 1;
73 
74     ngx_cached_time = &cached_time[0];
75 
76     ngx_time_update();
77 }
78 
79 
80 void
ngx_time_update(void)81 ngx_time_update(void)
82 {
83     u_char          *p0, *p1, *p2, *p3, *p4;
84     ngx_tm_t         tm, gmt;
85     time_t           sec;
86     ngx_uint_t       msec;
87     ngx_time_t      *tp;
88     struct timeval   tv;
89 
90     if (!ngx_trylock(&ngx_time_lock)) {
91         return;
92     }
93 
94     ngx_gettimeofday(&tv);
95 
96     sec = tv.tv_sec;
97     msec = tv.tv_usec / 1000;
98 
99     ngx_current_msec = ngx_monotonic_time(sec, msec);
100 
101     tp = &cached_time[slot];
102 
103     if (tp->sec == sec) {
104         tp->msec = msec;
105         ngx_unlock(&ngx_time_lock);
106         return;
107     }
108 
109     if (slot == NGX_TIME_SLOTS - 1) {
110         slot = 0;
111     } else {
112         slot++;
113     }
114 
115     tp = &cached_time[slot];
116 
117     tp->sec = sec;
118     tp->msec = msec;
119 
120     ngx_gmtime(sec, &gmt);
121 
122 
123     p0 = &cached_http_time[slot][0];
124 
125     (void) ngx_sprintf(p0, "%s, %02d %s %4d %02d:%02d:%02d GMT",
126                        week[gmt.ngx_tm_wday], gmt.ngx_tm_mday,
127                        months[gmt.ngx_tm_mon - 1], gmt.ngx_tm_year,
128                        gmt.ngx_tm_hour, gmt.ngx_tm_min, gmt.ngx_tm_sec);
129 
130 #if (NGX_HAVE_GETTIMEZONE)
131 
132     tp->gmtoff = ngx_gettimezone();
133     ngx_gmtime(sec + tp->gmtoff * 60, &tm);
134 
135 #elif (NGX_HAVE_GMTOFF)
136 
137     ngx_localtime(sec, &tm);
138     cached_gmtoff = (ngx_int_t) (tm.ngx_tm_gmtoff / 60);
139     tp->gmtoff = cached_gmtoff;
140 
141 #else
142 
143     ngx_localtime(sec, &tm);
144     cached_gmtoff = ngx_timezone(tm.ngx_tm_isdst);
145     tp->gmtoff = cached_gmtoff;
146 
147 #endif
148 
149 
150     p1 = &cached_err_log_time[slot][0];
151 
152     (void) ngx_sprintf(p1, "%4d/%02d/%02d %02d:%02d:%02d",
153                        tm.ngx_tm_year, tm.ngx_tm_mon,
154                        tm.ngx_tm_mday, tm.ngx_tm_hour,
155                        tm.ngx_tm_min, tm.ngx_tm_sec);
156 
157 
158     p2 = &cached_http_log_time[slot][0];
159 
160     (void) ngx_sprintf(p2, "%02d/%s/%d:%02d:%02d:%02d %c%02i%02i",
161                        tm.ngx_tm_mday, months[tm.ngx_tm_mon - 1],
162                        tm.ngx_tm_year, tm.ngx_tm_hour,
163                        tm.ngx_tm_min, tm.ngx_tm_sec,
164                        tp->gmtoff < 0 ? '-' : '+',
165                        ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60));
166 
167     p3 = &cached_http_log_iso8601[slot][0];
168 
169     (void) ngx_sprintf(p3, "%4d-%02d-%02dT%02d:%02d:%02d%c%02i:%02i",
170                        tm.ngx_tm_year, tm.ngx_tm_mon,
171                        tm.ngx_tm_mday, tm.ngx_tm_hour,
172                        tm.ngx_tm_min, tm.ngx_tm_sec,
173                        tp->gmtoff < 0 ? '-' : '+',
174                        ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60));
175 
176     p4 = &cached_syslog_time[slot][0];
177 
178     (void) ngx_sprintf(p4, "%s %2d %02d:%02d:%02d",
179                        months[tm.ngx_tm_mon - 1], tm.ngx_tm_mday,
180                        tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec);
181 
182     ngx_memory_barrier();
183 
184     ngx_cached_time = tp;
185     ngx_cached_http_time.data = p0;
186     ngx_cached_err_log_time.data = p1;
187     ngx_cached_http_log_time.data = p2;
188     ngx_cached_http_log_iso8601.data = p3;
189     ngx_cached_syslog_time.data = p4;
190 
191     ngx_unlock(&ngx_time_lock);
192 }
193 
194 
195 static ngx_msec_t
ngx_monotonic_time(time_t sec,ngx_uint_t msec)196 ngx_monotonic_time(time_t sec, ngx_uint_t msec)
197 {
198 #if (NGX_HAVE_CLOCK_MONOTONIC)
199     struct timespec  ts;
200 
201 #if defined(CLOCK_MONOTONIC_FAST)
202     clock_gettime(CLOCK_MONOTONIC_FAST, &ts);
203 
204 #elif defined(CLOCK_MONOTONIC_COARSE)
205     clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
206 
207 #else
208     clock_gettime(CLOCK_MONOTONIC, &ts);
209 #endif
210 
211     sec = ts.tv_sec;
212     msec = ts.tv_nsec / 1000000;
213 
214 #endif
215 
216     return (ngx_msec_t) sec * 1000 + msec;
217 }
218 
219 
220 #if !(NGX_WIN32)
221 
222 void
ngx_time_sigsafe_update(void)223 ngx_time_sigsafe_update(void)
224 {
225     u_char          *p, *p2;
226     ngx_tm_t         tm;
227     time_t           sec;
228     ngx_time_t      *tp;
229     struct timeval   tv;
230 
231     if (!ngx_trylock(&ngx_time_lock)) {
232         return;
233     }
234 
235     ngx_gettimeofday(&tv);
236 
237     sec = tv.tv_sec;
238 
239     tp = &cached_time[slot];
240 
241     if (tp->sec == sec) {
242         ngx_unlock(&ngx_time_lock);
243         return;
244     }
245 
246     if (slot == NGX_TIME_SLOTS - 1) {
247         slot = 0;
248     } else {
249         slot++;
250     }
251 
252     tp = &cached_time[slot];
253 
254     tp->sec = 0;
255 
256     ngx_gmtime(sec + cached_gmtoff * 60, &tm);
257 
258     p = &cached_err_log_time[slot][0];
259 
260     (void) ngx_sprintf(p, "%4d/%02d/%02d %02d:%02d:%02d",
261                        tm.ngx_tm_year, tm.ngx_tm_mon,
262                        tm.ngx_tm_mday, tm.ngx_tm_hour,
263                        tm.ngx_tm_min, tm.ngx_tm_sec);
264 
265     p2 = &cached_syslog_time[slot][0];
266 
267     (void) ngx_sprintf(p2, "%s %2d %02d:%02d:%02d",
268                        months[tm.ngx_tm_mon - 1], tm.ngx_tm_mday,
269                        tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec);
270 
271     ngx_memory_barrier();
272 
273     ngx_cached_err_log_time.data = p;
274     ngx_cached_syslog_time.data = p2;
275 
276     ngx_unlock(&ngx_time_lock);
277 }
278 
279 #endif
280 
281 
282 u_char *
ngx_http_time(u_char * buf,time_t t)283 ngx_http_time(u_char *buf, time_t t)
284 {
285     ngx_tm_t  tm;
286 
287     ngx_gmtime(t, &tm);
288 
289     return ngx_sprintf(buf, "%s, %02d %s %4d %02d:%02d:%02d GMT",
290                        week[tm.ngx_tm_wday],
291                        tm.ngx_tm_mday,
292                        months[tm.ngx_tm_mon - 1],
293                        tm.ngx_tm_year,
294                        tm.ngx_tm_hour,
295                        tm.ngx_tm_min,
296                        tm.ngx_tm_sec);
297 }
298 
299 
300 u_char *
ngx_http_cookie_time(u_char * buf,time_t t)301 ngx_http_cookie_time(u_char *buf, time_t t)
302 {
303     ngx_tm_t  tm;
304 
305     ngx_gmtime(t, &tm);
306 
307     /*
308      * Netscape 3.x does not understand 4-digit years at all and
309      * 2-digit years more than "37"
310      */
311 
312     return ngx_sprintf(buf,
313                        (tm.ngx_tm_year > 2037) ?
314                                          "%s, %02d-%s-%d %02d:%02d:%02d GMT":
315                                          "%s, %02d-%s-%02d %02d:%02d:%02d GMT",
316                        week[tm.ngx_tm_wday],
317                        tm.ngx_tm_mday,
318                        months[tm.ngx_tm_mon - 1],
319                        (tm.ngx_tm_year > 2037) ? tm.ngx_tm_year:
320                                                  tm.ngx_tm_year % 100,
321                        tm.ngx_tm_hour,
322                        tm.ngx_tm_min,
323                        tm.ngx_tm_sec);
324 }
325 
326 
327 void
ngx_gmtime(time_t t,ngx_tm_t * tp)328 ngx_gmtime(time_t t, ngx_tm_t *tp)
329 {
330     ngx_int_t   yday;
331     ngx_uint_t  sec, min, hour, mday, mon, year, wday, days, leap;
332 
333     /* the calculation is valid for positive time_t only */
334 
335     if (t < 0) {
336         t = 0;
337     }
338 
339     days = t / 86400;
340     sec = t % 86400;
341 
342     /*
343      * no more than 4 year digits supported,
344      * truncate to December 31, 9999, 23:59:59
345      */
346 
347     if (days > 2932896) {
348         days = 2932896;
349         sec = 86399;
350     }
351 
352     /* January 1, 1970 was Thursday */
353 
354     wday = (4 + days) % 7;
355 
356     hour = sec / 3600;
357     sec %= 3600;
358     min = sec / 60;
359     sec %= 60;
360 
361     /*
362      * the algorithm based on Gauss' formula,
363      * see src/core/ngx_parse_time.c
364      */
365 
366     /* days since March 1, 1 BC */
367     days = days - (31 + 28) + 719527;
368 
369     /*
370      * The "days" should be adjusted to 1 only, however, some March 1st's go
371      * to previous year, so we adjust them to 2.  This causes also shift of the
372      * last February days to next year, but we catch the case when "yday"
373      * becomes negative.
374      */
375 
376     year = (days + 2) * 400 / (365 * 400 + 100 - 4 + 1);
377 
378     yday = days - (365 * year + year / 4 - year / 100 + year / 400);
379 
380     if (yday < 0) {
381         leap = (year % 4 == 0) && (year % 100 || (year % 400 == 0));
382         yday = 365 + leap + yday;
383         year--;
384     }
385 
386     /*
387      * The empirical formula that maps "yday" to month.
388      * There are at least 10 variants, some of them are:
389      *     mon = (yday + 31) * 15 / 459
390      *     mon = (yday + 31) * 17 / 520
391      *     mon = (yday + 31) * 20 / 612
392      */
393 
394     mon = (yday + 31) * 10 / 306;
395 
396     /* the Gauss' formula that evaluates days before the month */
397 
398     mday = yday - (367 * mon / 12 - 30) + 1;
399 
400     if (yday >= 306) {
401 
402         year++;
403         mon -= 10;
404 
405         /*
406          * there is no "yday" in Win32 SYSTEMTIME
407          *
408          * yday -= 306;
409          */
410 
411     } else {
412 
413         mon += 2;
414 
415         /*
416          * there is no "yday" in Win32 SYSTEMTIME
417          *
418          * yday += 31 + 28 + leap;
419          */
420     }
421 
422     tp->ngx_tm_sec = (ngx_tm_sec_t) sec;
423     tp->ngx_tm_min = (ngx_tm_min_t) min;
424     tp->ngx_tm_hour = (ngx_tm_hour_t) hour;
425     tp->ngx_tm_mday = (ngx_tm_mday_t) mday;
426     tp->ngx_tm_mon = (ngx_tm_mon_t) mon;
427     tp->ngx_tm_year = (ngx_tm_year_t) year;
428     tp->ngx_tm_wday = (ngx_tm_wday_t) wday;
429 }
430 
431 
432 time_t
ngx_next_time(time_t when)433 ngx_next_time(time_t when)
434 {
435     time_t     now, next;
436     struct tm  tm;
437 
438     now = ngx_time();
439 
440     ngx_libc_localtime(now, &tm);
441 
442     tm.tm_hour = (int) (when / 3600);
443     when %= 3600;
444     tm.tm_min = (int) (when / 60);
445     tm.tm_sec = (int) (when % 60);
446 
447     next = mktime(&tm);
448 
449     if (next == -1) {
450         return -1;
451     }
452 
453     if (next - now > 0) {
454         return next;
455     }
456 
457     tm.tm_mday++;
458 
459     /* mktime() should normalize a date (Jan 32, etc) */
460 
461     next = mktime(&tm);
462 
463     if (next != -1) {
464         return next;
465     }
466 
467     return -1;
468 }
469