每天进步一点点——Linux系统中的时间处理

转载请说明出处:http://blog.csdn.net/cywosp/article/details/25839551

在程序中时间处理往往是一个麻烦的事,Linux系统提供了很多关于时间处理的函数,我们可以用这些函数来完成我们所需要的功能。那么在程序中一般会关心哪些时间问题呢?

  • 真实时间:程序运行的时间,即程序启动到程序消亡所用时间或程序运行到现在所经过的时间
  • 进程时间:一个进程所使用的CPU时间总量,适用于对程序、算法性能的检查或优化

本文只关注真实时间的处理与转换

一、Epoch

无论地理位置如何,Linux系统内部对时间的表示方式均是以自Epoch以来的秒数来度量的,Epoch即通用协调时间(UTC,以前也称为格林威治标准时间,或GMT)的1970年1月1日零点零分零秒。这大致也是UNIX系统问世的时间。该时间可存储于time_t类型的变量中。

在32位Linux系统中,time_t是一个有符号整数,可以表示的日期范围从1901年12月13日20时45分52秒至2038年1月19日03:14:07。因此,32位系统的机器都将面临2038年的问题,当然这我们不必担心,到了2038年,我相信这些系统早已升级为64位或者更多位的系统了。不过有些寿命更长的嵌入式设备,可能还会受到此问题的影响。

二、时间转换函数

如下图所示,存储于time_t变量的数值可以和其他时间格式相互转换,其中还包含打印输出。这些函数屏蔽了因时区、夏令时(DST)制和本地化等问题给转换所带来的种种复杂

三、函数详解

1. 系统调用gettimeofday(),用于tv指向的缓冲中返回日历时间,其声明如下

#include <sys/time.h>

// Return 0 on success, or -1 on error

int gettimeofday (struct timeval* tv, struct timezone* tz);

tv所指向的结构体如下

struct timeval {

time_t          tv_sec;          /* Seconds since 00:00:00,
1 Jan 1970 UTC*/

susecond_t   tv_usec;        /* Additional microseconds
(long int)*/

};

gettimeofday()函数提供到微秒级的精度并存储于tv_usec中,这样的精度对大多数程序来说已经能够满足。在x86-64平台上,gettimeofday()不是系统调用,而是在用户态实现的,没有上下文切换和陷入内核的开销,因此其速度是比较快的[1]。参数tz是一个历史产物,在早期的UNIX系统实现中使用其来获取系统的时区信息,目前已被废弃,因此在调用时始终置为NULL。

2. 系统调用time(),其返回自Epoch以来的秒数,声明如下

#include <time.h>

// Returns number of seconds since the Epoch, or (time_t)-1 on error

time_t time (time_t* timep);

如果timep参数不为NULL,那么还会将自Epoch以来的的秒数置于timep所指向的位置中。但是在日常使用中我们都采用简单的调用方式t = time (NULL)。从函数说明上可以得知gettimeofday()的精确度要比time()要高。

3.  time_t可用于存储Epoch到现在的秒数,这个数值非常不便于我们直接解释,因为我们更容易接受的是MM-DD-YY HH:MIN:SS的形式。Linux中提供了简单的ctime()函数来处理time_t的转换

#include <time.h>

// Returns pointer to statically allocated string terminated by newline and \0 on success,
or NULL on error

char* ctime (const time_t* timep);

把一个指向time_t的指针作为timep参数传入函数,将返回一个长达26个字节的字符串,内含标准格式的日期和时间,如下:

Wed May  14  15:22:34  2014

该字符串包含换行符合终止空字节各一。ctime()函数在进行行转换时会自动对本地时区和DST设置加以考虑。返回的字符串经由静态分配的,因此无需调用free函数,正因如此,该函数是不可重入的(非线程安全的),线程安全的版为ctime_r()。

4. time_t与struct tm之间的转换

struct tm {

int tm_sec;      /*Seconds
(0-60)*/

int tm_min;     /*Minites
(0-59)*/

int tm_hour;   /*Hours
(0-23)*/

int tm_mday;  /*Day
of the month (1-31)*/

int tm_mon;   /*Month
(1-12)*/

int tm_year;    /*Year
since 1900*/

int tm_wday;   /*Day
of the week (Sunday = 0)*/

int tm_yday;    /*Day
in the year (0-365; 1 Jan = 0)*/

int tm_isdst;    /*Daylight
saving time flag > 0: DST is in effect; = 0:
DST is not effect; < 0: DST information not available*/

};

结构体tm将日期和时间分解成多个独立的字段,这样能方便程序获取不同的字段值来处理。字段tm_sec的上限为60而不是59,这样的设计主要是考虑闰秒,偶尔用其将人类日历调整至精确的天文年(所谓的回归年)。如果程序中定义了_BSD_SOURCE测试宏,那么有glibc定义的tm结构还会包括两个字段,一个为long
int tm_gmtoff,用于表示时间超出UTC以东的秒数,一个为const char* tm_zone,用于表示时区的缩写(例如:CEST为欧洲中部夏令时间)。

gmtime()和localtime()两个函数可将time_t转换成struct tm。gmtime()直接将time_t分解成UTC时间的tm,localtime()需要考虑时区和夏令时的设置,具体声明如下:

#include <time.h>

// Both return a pointer to a statically allocated broker-down time structure on success,
or NULL on error

struct tm* gmtime (const time_t
*timep);

struct tm* localtime (const time_t
*timep);

以上两个函数都是非线程安全的,线程安全版本为gmtime_r()和localtime_r()

mktime()函数可以将struct tm转换成time_t,其声明如下:

#include <time.h>

// Returns seconds since the Epoch corresponding to timeptr on success, or (time_t)-1 on
error

time_t mktime (struct tm *timeptr);

该函数可能会修改timeptr对应的值,至少会确保对tm_wday和tm_yday字段的设置,确保这些字段与其他字段能够相互对应起来。同时,mktime()在进行转换时会对时区进行设置。此外,DST设置的使用与否取决于输入字段tm_isdst的值。

  • 若tm_isdst为0,则将这一时间视为标准时间(即,忽略夏令时)
  • 若tm_isdst大于0,则将这一时间视为夏令时
  • 若tm_isdst小于0,则试图判定DST在每年的这一时间是否生效。这往往是众望所归的设置

5. 将struct tm转化易于理解的字符串

#include <time.h>

// Returns pointer to statically allocated string terminated by newline and \0 on success,
or NULL on error

char* asctime (const struct tm
*timeptr);

与ctime()相比,本地时区的设置对asctime()没有影响。其返回的指针所指向的是由静态分配的字符串,因此其不是线程安全的,线程安全的版本为asctime_r(),输出结果大概如下:

Wed May  14  16:43:21 CET 2014

asctime()函数返回的是一个固定形式的字符串,有时为了更易于理解,程序不想仅局限于这样的字符串,于是Linux中提供了strftime()函数,声明如下:

#include <time.h>

// Returns number of bytes placed in outstr (excluding terminating null bytes) on success,
or 0 on error

size_t strftime (char *outstr, size_t maxsize, const
char *format, const struct tm *timeptr);

outstr中返回的字符串按照format参数定义的格式做了格式化,maxsize则是ourstr的最大长度,如果成功则将格式化后的内容写入outstr所指向的缓冲区中,然后返回字符串的真实长度,含终止空字节,如果真实长度超过了maxsize参数的大小,那么返回0以表示出错,且无法确定outstr的内容。参数format是一个字符串,类似于printf()参数。具体值在此不做详解,可以google。

strptime()函数与strftime()函数正好相反,其可以将包含日期和时间的字符串转换成struct tm,声明如下:

#define _XOPEN_SOURCE

#include <time.h>

// Returns pointer to next unprocessed character in str on success, or NULL on error

char* strptime (const char *str, const
char *format, struct tm *timeptr);

函数strptime()按照format参数内容对由日期和时间组成的字符串str加以解析,并将转换后的数值存储于timeptr所指的缓存中,如果成功,返回指向str中下一个还未解析过的字符,如果所给的str无法和format相对应,则会解析失败,返回NULL,以示错误。最后还需注意,strptime()函数不会设置tm中的tm_isdst字段。

四、时区

不同的国家使用不同的时区和夏时制,对于要输入输出时间的程序来说,必须对时区进行考虑,尤其是全球的分布式系统。出于时区信息太多,Linux系统没有直接将其编码于程序或函数库中,而是以标准格式存储于文件。这些文件位于/usr/share/zoneinfo目录里。系统的本地时区则由文件/etc/localtime定义,其通常是一个链接到/usr/share/zoneinfo下的文件。

时区文件格式记叙于tzfile(5)手册页,其创建可通过zic(8)(时区信息编译器,zone information compiler)工具来完成。zdump(8)命令可根据指定时区文件中的时区来显示当前时间

为运行的程序指定一个时区,需要将TZ环境变量设置为一个冒号(:)和时区名称组成的字符串,其中时区名称定义于/usr/share/zoneinfo中。时区的设置会影响到ctime()、localtime()、mktime()、strftime()等函数,为了获取时区设置,这些函数都会调用tzset(3)对如下全局变量进行设置:

char *tzname[2];     /*Name
of timezone and alternate (DST) timezone*/

int daylight;            /*Nonzero
if there is an alternate (DST) timezone*/

long timezone;       /*Seconds
difference between UTC and local standard time*/

五、总结

多种系统调用允许我们获取和设置系统的时间,以及一系列的库函数能够使我们完成各种时间表示法之间的转换。Linux是非实时的多任务操作系统,如果我们在程序中想完全精确的计时和定时时无法做到的,因为当前任务可能会被CPU随时切换出去,但是在日常的时间处理中,以上这些函数已经足够满足需求了。本文没有涉及到所讲述的函数用例,如有在程序中使用到这些函数,可以百度或者google,再者你可以查看https://github.com/ApusApp/Swift/tree/master/swift/base中关于时间处理的实现。

参考

[1] http://lwn.net/Articles/446528

每天进步一点点——Linux系统中的时间处理

时间: 2024-10-01 10:13:19

每天进步一点点——Linux系统中的时间处理的相关文章

每天进步一点点——Linux系统中的异常堆栈跟踪简单实现

转载请说明出处:http://blog.csdn.net/cywosp/article/details/25002201 在Linux中做C/C++开发经常会遇到一些不可预知的问题导致程序崩溃,同时崩溃后也没留下任何代码运行痕迹,因此,堆栈跟踪技术就显得非要重要了.本文将简单介绍Linux中C/C++程序运行时堆栈获取,首先来看backtrace系列函数--使用范围适合于没有安装GDB或者想要快速理清楚函数调用顺序的情况 ,头文件execinfo.h int backtrace (void **

老男孩教育每日一题-2017年5月11-基础知识点: linux系统中监听端口概念是什么?

1.题目 老男孩教育每日一题-2017年5月11-基础知识点:linux系统中监听端口概念是什么? 2.参考答案 监听端口的概念涉及到网络概念与TCP状态集转化概念,可能比较复杂不便理解,可以按照下图简单进行理解? 将整个服务器操作系统比喻作为一个别墅 服务器上的每一个网卡比作是别墅中每间房间 服务器网卡上配置的IP地址比喻作为房间中每个人 而房间里面人的耳朵就好比是监听的端口 当默认采用监听0.0.0.0地址时,表示房间中的每个人都竖起耳朵等待别墅外面的人呼唤当别墅外面的用户向房间1的人呼喊时

LINUX系统中动态链接库的创建与使用{补充}

大家都知道,在WINDOWS系统中有很多的动态链接库(以.DLL为后缀的文件,DLL即Dynamic Link Library).这种动态链接库,和静态函数库不同,它里面的函数并不是执行程序本身的一部分,而是根据执行程序需要按需装入,同时其执行代码可在多个执行程序间共享,节省了空间,提高了效率,具备很高的灵活性,得到越来越多程序员和用户的青睐.那么,在LINUX系统中有无这样的函数库呢? 答案是肯定的,LINUX的动态链接库不仅有,而且为数不少.在/lib目录下,就有许多以.so作后缀的文件,这

在Linux系统中文件(资源)和用户的管理

一个可执行二进制程序,被加载到内存,被内核调度到CPU上运行,这时候,就表现了一个进程.也可以说进程是程序的一个实例,是程序的动态表现. 在 Linux 系统中进程(process)是有属主的,也就是该进程以哪个用户的身份运行的.大家都知道,程序有输入和输出,也称这为程序IO.如果我们程序数据输入是磁盘.如,Web 服务器,接收用户的请求之后,把网页数据从磁盘中读入加工之后再把数据响应给用户.如果,发起Web 服务进程的用户没有读取该用户请求网页文件的权限.则无法响应用户的请求了.所以,文件(资

Linux系统中安装MySQL数据库操作手册

Linux系统中MySQL数据库安装手册 一.安装概述: 在Linux操作系统中安装MySQL数据库是一个我们必须要掌握的一门技术,也决定了你以后找工作的薪资待遇,所以你知道它的厉害了吧!学会安装只是第一步,你还得学好数据库的基本操作,以及搭建一个数据库的主从配置等等.这些我都会在后面的博文中说到.下面我就简单的写一些安装MySQL数据库的一些步骤,仅供参考,如有什么不妥的地方,还请见谅! 二.安装步骤: (一)基本准备工作 1.打开虚拟机,在虚拟机中下载好最新版的MySQL数据库的压缩包.在M

学习Linux系统中命令的简单方法

如果说如何快速学习.了解Linux的话,我的答案是学命令.背命令!为何呢?对于一名新手来说,去学习Linux的思想.了解Linux的架构.明白Linux中"一切皆文件"概念虽然说是没有错,是对的.但是个人认为去学习这些"高大上"的东西不是一时半会的事儿,它需要一定的时间和经验去沉淀才能掌握.那么如何最快速了解Linux并使用呢?我依然觉得学命令.背命令,掌握命令是比较笨但却是比较快的方式. 我开始学习Linux的时候,问了前辈:我入门Linux需要掌握哪些命令呢?前

LINUX系统中LV动态扩容

LINUX系统中LV扩容 服务器:192.168.26.209 扩容数据盘:原来服务器存储配置信息 df -h df –h 增加一个硬盘,扩容20G 启动服务器,系统已经识别新硬盘设备. 创建PV: [[email protected] ~]# pvcreate /dev/sdc [[email protected] ~]# pvs 先扩展VG: [[email protected] ~]# vgextend vg_centos /dev/sdc [[email protected] ~]# p

Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf 动态库的后缀为*.so 静态库的后缀为 libxxx.a ldconfig 目录名

Linux系统中“动态库”和“静态库”那点事儿 /etc/ld.so.conf  动态库的后缀为*.so  静态库的后缀为 libxxx.a   ldconfig   目录名 转载自:http://blog.chinaunix.net/uid-23069658-id-3142046.html 今天我们主要来说说Linux系统下基于动态库(.so)和静态(.a)的程序那些猫腻.在这之前,我们需要了解一下源代码到可执行程序之间到底发生了什么神奇而美妙的事情. 在Linux操作系统中,普遍使用ELF格

每天进步一点点——Linux系统时间来处理

转载请注明出处:http://blog.csdn.net/cywosp/article/details/25839551 在程序中时间处理往往是一个麻烦的事.Linux系统提供了非常多关于时间处理的函数,我们能够用这些函数来完毕我们所须要的功能.那么在程序中通常会关心哪些时间问题呢? 真实时间:程序执行的时间,即程序启动到程序消亡所用时间或程序执行到如今所经过的时间 进程时间:一个进程所使用的CPU时间总量.适用于对程序.算法性能的检查或优化 本文仅仅关注真实时间的处理与转换 一.Epoch 不