3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
13 * The time may be updated by signal handler or by several threads.
14 * The time update operations are rare and require to hold the ngx_time_lock.
15 * The time read operations are frequent, so they are lock-free and get time
16 * values and strings from the current slot. Thus thread may get the corrupted
17 * values only if it is preempted while copying and then it is not scheduled
18 * to run more than NGX_TIME_SLOTS seconds.
21 #define NGX_TIME_SLOTS 64
23 static ngx_uint_t slot
;
24 static ngx_atomic_t ngx_time_lock
;
26 volatile ngx_msec_t ngx_current_msec
;
27 volatile ngx_time_t
*ngx_cached_time
;
28 volatile ngx_str_t ngx_cached_err_log_time
;
29 volatile ngx_str_t ngx_cached_http_time
;
30 volatile ngx_str_t ngx_cached_http_log_time
;
31 volatile ngx_str_t ngx_cached_http_log_iso8601
;
36 * localtime() and localtime_r() are not Async-Signal-Safe functions, therefore,
37 * they must not be called by a signal handler, so we use the cached
38 * GMT offset value. Fortunately the value is changed only two times a year.
41 static ngx_int_t cached_gmtoff
;
44 static ngx_time_t cached_time
[NGX_TIME_SLOTS
];
45 static u_char cached_err_log_time
[NGX_TIME_SLOTS
]
46 [sizeof("1970/09/28 12:00:00")];
47 static u_char cached_http_time
[NGX_TIME_SLOTS
]
48 [sizeof("Mon, 28 Sep 1970 06:00:00 GMT")];
49 static u_char cached_http_log_time
[NGX_TIME_SLOTS
]
50 [sizeof("28/Sep/1970:12:00:00 +0600")];
51 static u_char cached_http_log_iso8601
[NGX_TIME_SLOTS
]
52 [sizeof("1970-09-28T12:00:00+06:00")];
55 static char *week
[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
56 static char *months
[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
57 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
62 ngx_cached_err_log_time
.len
= sizeof("1970/09/28 12:00:00") - 1;
63 ngx_cached_http_time
.len
= sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1;
64 ngx_cached_http_log_time
.len
= sizeof("28/Sep/1970:12:00:00 +0600") - 1;
65 ngx_cached_http_log_iso8601
.len
= sizeof("1970-09-28T12:00:00+06:00") - 1;
67 ngx_cached_time
= &cached_time
[0];
76 u_char
*p0
, *p1
, *p2
, *p3
;
83 if (!ngx_trylock(&ngx_time_lock
)) {
87 ngx_gettimeofday(&tv
);
90 msec
= tv
.tv_usec
/ 1000;
92 ngx_current_msec
= (ngx_msec_t
) sec
* 1000 + msec
;
94 tp
= &cached_time
[slot
];
98 ngx_unlock(&ngx_time_lock
);
102 if (slot
== NGX_TIME_SLOTS
- 1) {
108 tp
= &cached_time
[slot
];
113 ngx_gmtime(sec
, &gmt
);
116 p0
= &cached_http_time
[slot
][0];
118 (void) ngx_sprintf(p0
, "%s, %02d %s %4d %02d:%02d:%02d GMT",
119 week
[gmt
.ngx_tm_wday
], gmt
.ngx_tm_mday
,
120 months
[gmt
.ngx_tm_mon
- 1], gmt
.ngx_tm_year
,
121 gmt
.ngx_tm_hour
, gmt
.ngx_tm_min
, gmt
.ngx_tm_sec
);
123 #if (NGX_HAVE_GETTIMEZONE)
125 tp
->gmtoff
= ngx_gettimezone();
126 ngx_gmtime(sec
+ tp
->gmtoff
* 60, &tm
);
128 #elif (NGX_HAVE_GMTOFF)
130 ngx_localtime(sec
, &tm
);
131 cached_gmtoff
= (ngx_int_t
) (tm
.ngx_tm_gmtoff
/ 60);
132 tp
->gmtoff
= cached_gmtoff
;
136 ngx_localtime(sec
, &tm
);
137 cached_gmtoff
= ngx_timezone(tm
.ngx_tm_isdst
);
138 tp
->gmtoff
= cached_gmtoff
;
143 p1
= &cached_err_log_time
[slot
][0];
145 (void) ngx_sprintf(p1
, "%4d/%02d/%02d %02d:%02d:%02d",
146 tm
.ngx_tm_year
, tm
.ngx_tm_mon
,
147 tm
.ngx_tm_mday
, tm
.ngx_tm_hour
,
148 tm
.ngx_tm_min
, tm
.ngx_tm_sec
);
151 p2
= &cached_http_log_time
[slot
][0];
153 (void) ngx_sprintf(p2
, "%02d/%s/%d:%02d:%02d:%02d %c%02d%02d",
154 tm
.ngx_tm_mday
, months
[tm
.ngx_tm_mon
- 1],
155 tm
.ngx_tm_year
, tm
.ngx_tm_hour
,
156 tm
.ngx_tm_min
, tm
.ngx_tm_sec
,
157 tp
->gmtoff
< 0 ? '-' : '+',
158 ngx_abs(tp
->gmtoff
/ 60), ngx_abs(tp
->gmtoff
% 60));
160 p3
= &cached_http_log_iso8601
[slot
][0];
162 (void) ngx_sprintf(p3
, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
163 tm
.ngx_tm_year
, tm
.ngx_tm_mon
,
164 tm
.ngx_tm_mday
, tm
.ngx_tm_hour
,
165 tm
.ngx_tm_min
, tm
.ngx_tm_sec
,
166 tp
->gmtoff
< 0 ? '-' : '+',
167 ngx_abs(tp
->gmtoff
/ 60), ngx_abs(tp
->gmtoff
% 60));
170 ngx_memory_barrier();
172 ngx_cached_time
= tp
;
173 ngx_cached_http_time
.data
= p0
;
174 ngx_cached_err_log_time
.data
= p1
;
175 ngx_cached_http_log_time
.data
= p2
;
176 ngx_cached_http_log_iso8601
.data
= p3
;
178 ngx_unlock(&ngx_time_lock
);
185 ngx_time_sigsafe_update(void)
193 if (!ngx_trylock(&ngx_time_lock
)) {
197 ngx_gettimeofday(&tv
);
201 tp
= &cached_time
[slot
];
203 if (tp
->sec
== sec
) {
204 ngx_unlock(&ngx_time_lock
);
208 if (slot
== NGX_TIME_SLOTS
- 1) {
214 tp
= &cached_time
[slot
];
218 ngx_gmtime(sec
+ cached_gmtoff
* 60, &tm
);
220 p
= &cached_err_log_time
[slot
][0];
222 (void) ngx_sprintf(p
, "%4d/%02d/%02d %02d:%02d:%02d",
223 tm
.ngx_tm_year
, tm
.ngx_tm_mon
,
224 tm
.ngx_tm_mday
, tm
.ngx_tm_hour
,
225 tm
.ngx_tm_min
, tm
.ngx_tm_sec
);
227 ngx_memory_barrier();
229 ngx_cached_err_log_time
.data
= p
;
231 ngx_unlock(&ngx_time_lock
);
238 ngx_http_time(u_char
*buf
, time_t t
)
244 return ngx_sprintf(buf
, "%s, %02d %s %4d %02d:%02d:%02d GMT",
245 week
[tm
.ngx_tm_wday
],
247 months
[tm
.ngx_tm_mon
- 1],
256 ngx_http_cookie_time(u_char
*buf
, time_t t
)
263 * Netscape 3.x does not understand 4-digit years at all and
264 * 2-digit years more than "37"
267 return ngx_sprintf(buf
,
268 (tm
.ngx_tm_year
> 2037) ?
269 "%s, %02d-%s-%d %02d:%02d:%02d GMT":
270 "%s, %02d-%s-%02d %02d:%02d:%02d GMT",
271 week
[tm
.ngx_tm_wday
],
273 months
[tm
.ngx_tm_mon
- 1],
274 (tm
.ngx_tm_year
> 2037) ? tm
.ngx_tm_year
:
275 tm
.ngx_tm_year
% 100,
283 ngx_gmtime(time_t t
, ngx_tm_t
*tp
)
286 ngx_uint_t n
, sec
, min
, hour
, mday
, mon
, year
, wday
, days
, leap
;
288 /* the calculation is valid for positive time_t only */
294 /* January 1, 1970 was Thursday */
296 wday
= (4 + days
) % 7;
305 * the algorithm based on Gauss' formula,
306 * see src/http/ngx_http_parse_time.c
309 /* days since March 1, 1 BC */
310 days
= days
- (31 + 28) + 719527;
313 * The "days" should be adjusted to 1 only, however, some March 1st's go
314 * to previous year, so we adjust them to 2. This causes also shift of the
315 * last February days to next year, but we catch the case when "yday"
319 year
= (days
+ 2) * 400 / (365 * 400 + 100 - 4 + 1);
321 yday
= days
- (365 * year
+ year
/ 4 - year
/ 100 + year
/ 400);
324 leap
= (year
% 4 == 0) && (year
% 100 || (year
% 400 == 0));
325 yday
= 365 + leap
+ yday
;
330 * The empirical formula that maps "yday" to month.
331 * There are at least 10 variants, some of them are:
332 * mon = (yday + 31) * 15 / 459
333 * mon = (yday + 31) * 17 / 520
334 * mon = (yday + 31) * 20 / 612
337 mon
= (yday
+ 31) * 10 / 306;
339 /* the Gauss' formula that evaluates days before the month */
341 mday
= yday
- (367 * mon
/ 12 - 30) + 1;
349 * there is no "yday" in Win32 SYSTEMTIME
359 * there is no "yday" in Win32 SYSTEMTIME
361 * yday += 31 + 28 + leap;
365 tp
->ngx_tm_sec
= (ngx_tm_sec_t
) sec
;
366 tp
->ngx_tm_min
= (ngx_tm_min_t
) min
;
367 tp
->ngx_tm_hour
= (ngx_tm_hour_t
) hour
;
368 tp
->ngx_tm_mday
= (ngx_tm_mday_t
) mday
;
369 tp
->ngx_tm_mon
= (ngx_tm_mon_t
) mon
;
370 tp
->ngx_tm_year
= (ngx_tm_year_t
) year
;
371 tp
->ngx_tm_wday
= (ngx_tm_wday_t
) wday
;
376 ngx_next_time(time_t when
)
383 ngx_libc_localtime(now
, &tm
);
385 tm
.tm_hour
= (int) (when
/ 3600);
387 tm
.tm_min
= (int) (when
/ 60);
388 tm
.tm_sec
= (int) (when
% 60);
396 if (next
- now
> 0) {
402 /* mktime() should normalize a date (Jan 32, etc) */