这里主要讲述linux中时间和时区相关的原理。包括设置方法、配置、环境变量、以及库函数,及应用举例。
基本概念
系统时钟和硬件时钟
Linux时钟分为系统时钟 (System Clock)和硬件(Real Time Clock,简称RTC)时钟。系统时钟是指当前Linux Kernel中的时钟,而硬件时钟则是主板上由电池供电的时钟,这个硬件时钟可以在BIOS中进行设置。当Linux启动时,硬件时钟会去读取系统时钟的设置,然后系统时钟就会独立于硬件运作。
硬件时间和系统时间的同步
重新启动系统,硬件时间会读取系统时间,实现同步,但是在不重新启动的时候,需要用hwclock或clock命令实现同步。
时间和时区
UTC:协调世界时,又称世界标准时间,简称UTC,从英文国际时间/法文协调时间”Universal Time/Temps Cordonné”而来。
时区:反映与UTC的时差问题,中国大陆、香港、澳门、台湾、蒙古国、新加坡、马来西亚、菲律宾、澳洲西部的时间与UTC的时差均为+8,也就是UTC+8。各个地区的时间都是UTC结合地区的时区信息得到的。
整个地球分为二十四时区,每个时区都有自己的本地时间,他们的关系如下:
-
UTC时间 与 GMT时间
我们可以认为格林威治时间就是时间协调时间(GMT = UTC),格林威治时间和UTC时间都用秒数来计算的。
-
UTC时间与本地时间
UTC + 时区差 = 本地时间
时区差东为正,西为负。
-
UTC与Unix时间戳
在计算机中看到的UTC时间都是从(1970年01月01日 0:00:00)开始计算秒数的。所看到的UTC时间那就是从1970年这个时间点起到具体时间共有多少秒。 这个秒数就是Unix时间戳。
例如:
本地(北京)在东八区, 东八区时区差记为 +0800,则:本地(北京)时间 = UTC + (+0800)
所以,不同地区, 其本地时间各不相同。
再如, 对于中国和格林,这两个地区的时区不同,得到的时间也不同。分别如下:
CST:中国标准时间(China Standard Time),与UTC时差为8,这个解释可能是针对RedHat Linux。
GMT:格林尼治标准时间(旧译格林威治平均时间或格林威治标准时间;英语:Greenwich Mean Time,GMT)是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义在通过那里的经线。
常用命令
下面给出的是常见的命令,不同系统可能有不同的命令。
有关系统时间的命令: data,ntpupdate
有关硬件时钟的命令: hwclock/clock
时区相关命令: tzselect
配置文件与环境变量
时区信息一般首先从环境变量中去读,如果没有再从 /etc/TZ
等配置文件中去读。
所涉及的配置文件和环境变量可能会根据系统有所不同,一般情况如下:
时区的配置文件: etc/localtime (可以是/usr/share/zoneinfo…的软链接), /etc/TZ, /etc/sysconfig/clock
-
环境变量:
TZ
, 使用举例如下:bash中 export TZ="Europe/Moscow" date -u -s "2011-10-29 21:55:00" cshell中 setenv TZ Europe/Moscow
库函数
这些为有关时间时区的常见系统库函数。
-
time_t time(time_t *t)
返回
time_t
类型,值为UTC时间从0时0分0秒算起到现在所经过的秒数。 -
struct tm*gmtime(const time_t*timep)
根据传入的
time_t
返回类型为struct tm
结构,包含时、分、秒、日、周、月、年、日光节约时间标记等信息的时间表示,该时间为GMT时间,未经时区转换。 -
struct tm *localtime(const time_t * timep)
根据传入的
time_t
返回类型为struct tm
结构,包含时、分、秒、日、周、月、年、日光节约时间标记等信息的时间表示,该时间为当地时间,已经时区转换。 -
time_t mktime(struct tm * timeptr)
将
struct tm
类型的时间转换为time_t
类型,值为UTC时间从0时0分0秒算起到现在所经过的秒数。 -
char *ctime(const time_t *timep)
将传入的
time_t
类型时间转化为现实世界中的字符串表示形式,例如:Fri, 16 Oct 2015 23:12:18 +0800
-
char * asctime(const struct tm * timeptr)
将传入的
struct tm
结构类型时间转化为现实世界中的字符串表示形式,例如:Fri Oct 16 15:15:54 2015
-
int gettimeofday ( struct timeval * tv , struct timezone * tz )
返回当前时间信息,
struct timeval *tv
存放秒和微秒信息,struct timezone *tz
存放时区信息。 -
int settimeofday ( const struct timeval *tv,const struct timezone *tz)
根据传入的时间信息
struct timeval *tv
, 和时区信息struct timezone *tz
设置当前时间。
总之,time_t 是用秒数表示时间一般在系统时间戳中使用(一般就是UTC时间);struct tm 使用结构体表示时间,在程序中使用(不同时区可能会有不同值);而现实中使用字符串来表示时间,字符串格式可以定制。时区信息一般首先从环境变量中去读,如果没有再从 /etc/TZ
等配置文件中去读。
应用举例
在单一进程中修改时区的方法
setenv("TZ","Europe/Moscow",1);
tzset();
时间同步校准
假设安装完系统发现时间与现实时间相差8小时。
一般是如下原因:安装系统时选择的时区是上海,而系统启动后把bios时间认为是utc时间,再根据上海所处时区,将时间+8小时给我们。这个时候的bios的时间和软件的时间便出现不一致的情况。一个代表 utc 一个代表我们设置的cst(+8时区)。
可做如下调整(例如系统为center os 6):
-
修改配置文件,将默认环境变量更正为上海所在时区,不使用UTC时间
$ vi /etc/sysconfig/clock ZONE="Asia/Shanghai" UTC=false ARC=false
这个文件影响系统启动后默认的时间相关环境变量值,这里主要是
UTC=false
这个选项,设置硬件时钟不是跟utc一致。 -
将linux的时区设置为 上海
$ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
加载配置时间环境变量的文件之前,需要指定好该文件的软链接位置,通过这个可在各个时区配置文件下通用的
/etc/localtime
软链接,可使系统能够找到刚刚设置的时区配置文件,并设置好相应的环境变量。 -
对准时间ntp
$ntpdate 192.43.244.18
系统启动之后,系统时间通过
ntpdate
工具在网络上获取到。 -
设置硬件时间和软件时间的一致并校准
/sbin/hwclock -systohc
通过这个命令,将系统时间同步至硬件始终上,这样我们的linux软件和计算机硬件都是cst时间了--并且是我们设置的上海时区。
其它
实际问题的分析案例
问题描述
在设置好国家信息之后, 主页上的时间信息并没有根据时区进行调整。重启之后,有时候会管用。
设置时间的位置其情况是:通过 putenv()
设置好 TZ
环境变量,再通过 tzset()
设置好时区信息。但是最后通过 time()
和 localtime()
函数得到的时间信息只在进程1中起作用。
需要确认的问题
- 环境变量的获取与设置
- 时间/时区/时钟的概念
- 有关时区的环境变量/配置文件/调用接口
分析原因
代码还没有深入看过,根据前面的原理分析有如下可能的原因:
有两个进程, 设置时间的进程, 和显示时间的进程。
在设置时间的进程中设置时区环境变量
TZ
,时区信息在设置时间的进程中起作用;但是设置好时区信息后,显示时间的进程仍然根据原来的环境变量TZ
显示时间,所以显示的时间和设置的时区不一致。需要确认的是:确认设置时间的进程设置后环境变量后,设置的值是否已正确保存到合适的位置;显示时间的进程是否会及时更新环境变量并显示,并且是否是基于设置时间进程保存的配置文件中读取信息、更新环境变量再进行显示。
参考
man 3 tzset