ngx_localtime
函数
声明
在 src\os\unix\ngx_time.h 中:
void ngx_localtime(time_t s, ngx_tm_t *tm);
定义
在 src/os/unix/ngx_time.c 中
void ngx_localtime(time_t s, ngx_tm_t *tm) { #if (NGX_HAVE_LOCALTIME_R) (void) localtime_r(&s, tm); #else ngx_tm_t *t; t = localtime(&s); *tm = *t; #endif tm->ngx_tm_mon++; tm->ngx_tm_year += 1900; }
ngx_localtime
函数的主要作用是将时间戳
time_t
转换为本地时间结构体ngx_tm_t
的函数实现。它的主要作用是将一个 Unix 时间戳(自 1970 年 1 月 1 日以来的秒数)转换为本地时间,并对结果进行一些调整,使其更符合人类可读的时间格式将传入的
time_t
类型的时间戳s
转换为本地时间。将转换后的本地时间存储到
ngx_tm_t
类型的结构体tm
中。对转换后的月份和年份字段进行调整,以符合常见的日期表示方式。
#if (NGX_HAVE_LOCALTIME_R) (void) localtime_r(&s, tm); #else ngx_tm_t *t; t = localtime(&s); *tm = *t; #endif
条件编译判断系统是否支持
localtime_r
函数通过宏
NGX_HAVE_LOCALTIME_R
判断当前系统是否支持线程安全的localtime_r
函数。如果支持
localtime_r
:直接调用localtime_r
函数将time_t
类型的时间戳s
转换为本地时间,并将结果存储在ngx_tm_t
结构体tm
中。(void)
强制类型转换用于避免编译器产生未使用返回值的警告。如果不支持
localtime_r
:使用非线程安全的localtime
函数进行时间转换。
localtime
返回一个指向静态内存的指针,因此在多线程环境中可能会引发竞争问题。为了确保线程安全,这里将返回的结果复制到用户提供的
tm
结构体中,避免直接依赖静态内存。objs/ngx_auto_config.h:282:#define NGX_HAVE_LOCALTIME_R 1
在 objs/ngx_auto_config.h 中:
#ifndef NGX_HAVE_LOCALTIME_R #define NGX_HAVE_LOCALTIME_R 1 #endif
所以
#if (NGX_HAVE_LOCALTIME_R) (void) localtime_r(&s, tm);
这部分成立,使用 localtime_r
可用 gcc -E 将宏展开来确认
gcc -E src/os/unix/ngx_time.c \ -I src/core \ -I src/event \ -I src/event/modules \ -I src/os/unix \ -I objs \ > ngx_time_preprocessed.c
在输出文件 ngx_time_preprocessed.c 中找到
ngx_localtime
函数void ngx_localtime(time_t s, ngx_tm_t *tm) { (void) localtime_r(&s, tm); # 70 "src/os/unix/ngx_time.c" tm->tm_mon++; tm->tm_year += 1900; }
的确使用的是 localtime_r
调整月份和年份
tm->ngx_tm_mon++; tm->ngx_tm_year += 1900;
- 调整月份:
struct tm
结构体中的tm_mon
成员表示月份,其取值范围是 0 - 11(0 表示 1 月,11 表示 12 月)。为了符合常规的月份表示方式(1 - 12),将ngx_tm_mon
加 1。- 年份调整 :返回的年份是从 1900 年开始的偏移量(例如,2023 年对应的是 123)。为了得到完整的年份,需要加上 1900。
localtime_r
localtime_r
是一个线程安全的函数,用于将时间戳(time_t
类型)转换为本地时间,并将结果存储到用户提供的struct tm
结构体中。它是 POSIX 标准定义的函数之一,广泛用于多线程环境下的时间处理函数原型
struct tm *localtime_r(const time_t *timep, struct tm *result);
timep
:指向一个time_t
类型的时间戳,表示自 1970 年 1 月 1 日(UTC 时间)以来的秒数。result
:指向一个用户提供的struct tm
结构体,用于存储转换后的本地时间。返回值 :
- 成功时,返回
result
的指针(即输入的第二个参数)。- 失败时,返回
NULL
,通常是因为输入的时间戳无效或系统资源不足与
localtime
函数的区别
localtime
函数也用于将time_t
时间戳转换为本地时间,但它是非线程安全的。localtime
函数内部使用一个静态分配的struct tm
结构体来存储转换结果,这意味着在多线程环境下,如果多个线程同时调用localtime
函数,它们可能会覆盖彼此的结果,从而导致数据竞争和不一致的问题。而
localtime_r
函数是线程安全的,因为它需要用户传入一个struct tm
结构体的指针,函数会将转换结果直接填充到这个用户提供的结构体中,不同线程可以使用不同的struct tm
结构体实例,避免了数据竞争的问题。转换后的本地时间会被分解并填充到
struct tm
的各个字段中,例如年、月、日、小时、分钟等
localtime_r函数转换后的月份字段
它的取值范围是 0 到 11
这里 0 代表 1 月,1 代表 2 月,以此类推,11 代表 12 月
这种表示方式是为了与数组索引的习惯保持一致,因为在很多计算中,从 0 开始计数更方便。
localtime_r函数转换后的年份字段
它的值是从 1900 年开始的偏移量 。
例如:
tm_year = 123
表示 2023 年 (因为 1900 + 123 = 2023)。
tm_year = 0
表示 1900 年 。
ngx_gmtime
和ngx_localtime
这两个函数虽然表面上看起来作用相似(都是将时间戳转换为时间结构体),但它们的实际用途和行为是有区别的
ngx_gmtime
:格林威治标准时间(GMT)功能:将时间戳转换为 GMT 时间(UTC+0),与时区无关
使用场景:生成 HTTP 响应头中的时间(如
Date
字段),需遵循 GMT 标准
ngx_localtime
:本地时间功能:将时间戳转换为本地时间(依赖系统时区或配置)
表示的是 服务器所在时区的本地时间 。
使用场景:用于日志记录、调试信息等需要显示本地时间的场景
关键区别
函数 时区处理 主要用途 ngx_gmtime
固定为 GMT(UTC+0) HTTP 协议时间、跨时区一致性 ngx_localtime
依赖系统或配置的时区 本地日志、用户可见时间 为什么需要两个函数?
(1) HTTP 协议的要求
HTTP 协议中某些头部字段(如
Date
)明确要求使用 UTC 时间。例如:
Date: Sun, 01 Oct 2023 12:00:00 GMT
在这种情况下,必须使用
ngx_gmtime
来生成符合标准的时间字符串。(2) 日志记录的需求
Nginx 的日志文件通常需要记录事件发生的本地时间,以便管理员更容易理解日志内容。
例如:
2023/10/01 20:00:00 [error] ...
在这种情况下,使用
ngx_localtime
更加合适,因为它可以反映服务器所在时区的时间。(3) 避免时区混淆
通过区分
ngx_gmtime
和ngx_localtime
,Nginx 能够在不同场景下正确处理时间,避免因时区差异导致的错误或混淆。例如:
- 如果所有时间都使用本地时间,可能会导致跨时区部署的系统出现不一致。
- 如果所有时间都使用 UTC 时间,可能会让管理员难以理解日志中的时间信息。