10. Linux的时间

在一个程序之内,我们可能对两类时间会很感兴趣:

  • 真实时间Real time:这是一个要么从某个标准点开始测量的时间(日历时间)或者从某一个固定点(进程起始)开始测量的process已经过的时间。获得日历时间对于程序来说非常有用,比如说,对于时间戳数据记录或者文件。对于程序来说测量已经过的时间对于那些采取周期性动作或者在外部输入设备上做一定的时间测量非常有用。
  • 进程时间Process time:被进程所用的CPU时间。测量进程时间对于检查或者优化程序或者算法的表现非常有用。

大多数计算机架构都有一个自己的内建硬件时钟允许内核测量真实和进程时间。这一章,我们会看几个处理这两种类型时间的系统调用还有库函数来在人类可理解的和内部表示的时间之间做转换。因为人类可读的时间形式是依赖于地理位置还有语言文化传统,当讨论这些形式的时候就会引导我们进入时区和位置信息。

10.1 日历时间

不考虑地理位置,UNIX系统内部时间是从Epoch开始以秒计时。Epoch时间也就是1970年1月1日午夜,UTC时间。这个时间是UNIX系统开始工作的时候。日历时间被存储在time_t变量当中,SUSv3中规定是一个整型。

小提示:如果是在使用Linux32位系统的话,那么它的time_t是一个带符号的整型数字,它只能用来表示1901年12月31日20:45:52到2038年1月19日 03:14:07这个时间区间的时间。因此,有很多32位的UNIX系统都会面临到一个2038年问题,但如果他们在2038年之前也许计算一些日期晚于2038年的话,那么它们将会提前遇到这样的问题。但是到2038年的时候也许大多数Linux系统就已经是64位的了。

使用gettimeofday()系统调用就可以得到被bv指针指向的日历时间。

#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
//Returns 0 on success, or –1 on error

而这个timeval是一个结构体类型,它的样子如下:

struct timeval {
 time_t tv_sec; /* Seconds since 00:00:00, 1 Jan 1970 UTC */
 suseconds_t tv_usec; /* Additional microseconds (long int) */
};

虽然tv_usec是一个单位是微秒us的数据,但是它的精确度是由架构所决定的。在现代x86-32系统上,gettimeofday()确实可以提供微秒级准确度信息。

tz参数之于ggettimeofday()只是一个历史遗迹。在早期UNIX实现中,它被用于获取系统上的时区信息。现在这个参数已经不再有用而且一般来讲需要设置成NULL。

time()系统调用返回从Epoch开始所经过的秒数,其实也就是gettimeofday()中返回的tv数组中的tv_sec。

#include <time.h>
time_t time(time_t *timep);
//Returns number of seconds since the Epoch,or (time_t) –1 on error

如果timep参数不是NULL的话,那么从Epoch开始的秒数也就同样会被放在timep所指向的指针变量中。

尽管我们有两种获得该时间的可能性,但是考虑到如果给定的指针是个无效指针的话,这时候就会返回一个错误。为了能够简单安全使用,最好的办法还是设置timep为NULL。

t = time(NULL);

10.2 时间转换函数

下午列出了在time_t时间和别的时间形式之间的转换。这些函数帮助我们解决时区,地理等等复杂问题。

 10.2.1 转换time_t到可读形式

ctime()函数提供了一个简单的方式来转换time_t数据到可读形式上:

#include <time.h>
char *ctime(const time_t *timep);
//Returns pointer to statically allocated string terminated
//by newline and \0 on success, or NULL on error

给定一个指向time_t值的指针timep,ctime()就会返回一个26字节长的字符串包含了标准化形式的日期和时间,就像这样--Wed Jun 8 14:22:34 2011

该字符串包含了"\0\n"。ctime()函数会在做转换的时候自动考虑上地区时区因素。所返回的字符串也是静态分配的,所以未来对ctime()的调用就会改写该指针所指向的内容。

SUSv3当中任何对ctime(),gmtime(),localtime()或者asctime()的调用都会改写该静态分配的结构。换句话说,这些函数可能都使用者同一个字符串数组以及tm结构,不过这个取决于glibc的版本。如果我们想要保存所收到的信息而不被改写,最好的办法就是将他们在本地保存。

10.2.2 转换time_t和分解时间broken down time

gmtime()和localtime()函数转换一个time_t的值到所谓的broken-down time上。分解时间broken down time是一个放置在静态结构中的,会被以指针形式返回的一个结构体变量。

#include <time.h>
struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);
//Both return a pointer to a statically allocated broken-down
//time structure on success, or NULL on error

gmtime()转换日历时间到UTC所对应分解时间上(gm这里的意思就代表这格林威治时间)。相反,localtime()会考虑时区和DST设置并返回相应当地的分解时间。Reentrant版本分别是gmtime_r()和localtime_r()。

tm结构体包含着日期和时间域,他们被分解成为独立的部分:

struct tm {
 int tm_sec; /* Seconds (0-60) */
 int tm_min; /* Minutes (0-59) */
 int tm_hour; /* Hours (0-23) */
 int tm_mday; /* Day of the month (1-31) */
 int tm_mon; /* Month (0-11) */
 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_sec域可以上到60s而不是仅仅59秒,这是因为这里可能会考虑到闰秒的使用。

如果_BSD_SOURCE被定义的话,那么glibc所定义的tm结构就还会多两个域:

  1. long int tm_gmtoff 包含了和UTC时间之间秒的差值
  2. const char *tm_zone,也就是时区的简写

这些信息并不是SUSv3标准,而且也只在少量UNIX中实现。

mktime()则可以帮助从broken-down时间,也就是local time上转换到time_t值上。调用者将指向分解时间函数结构体的指针发送给mktime,则就可以在返回值里得到对应的time_t值。在这个转换当中,tm_wday和tm_yday会被忽略。

#include <time.h>
time_t mktime(struct tm *timeptr);
//Returns seconds since the Epoch corresponding to timeptr
//on success, or (time_t) –1 on error

mktime()函数也可以修改timeptr指向的结构体。至少来说,它可以确保tm_wday和tm_yday域会被设置到与其它对应输入数据相对应的正确的值上面。

另外mktime()如果发现有一些数据超出范围,它会根据其他信息去改写和调整。

比如,如果输入的tm_sec是123,之后它就会被改写成3,但是同时tm_min会被加2以保证绝对时间没有被改变。当然这个也可以用于当tm_sec为负数的情况。

  • 如果tm_isdst是0,那么这个时间就是标准时间
  • 如果tm_isdst大于0,那么这个时间就是DST
  • 如果tm_isdst小于0,就会尝试确定是否DST会影响该年的时间。

10.2.3 转换分解时间和可读时间

转换分解时间到可读时间

给定一个指向分解时间结构体的指针,asctime()返回一个指向静态分配字符串,其中包含了与ctime()调用返回相同的时间形式。

#include <time.h>
char *asctime(const struct tm *timeptr);
//Returns pointer to statically allocated string terminated by
//newline and \0 on success, or NULL on error

和ctime()对比,对于asctime()来说本地时区设置并没有什么影响,因为它只是转换一个分解时间,而这个分解时间本身已经是包含时区信息了。

当然我们对asctime()返回的形式没有控制权。可再入的asctime()版本是asctime_r()。

下面的例子给出了asctime()的使用方法,还有所有的这章里面会涉及到的时间转换函数。程序去除现在的日历时间,然后将使用不同的时间转换函数再将他们的结果展示出来。下面是一个运行在慕尼黑的程序的例子,它使用中欧时间CET,早于UTC一个小时。

time/calendar_time.c (from "The Linux Programming Interface")

#include <locale.h>
#include <time.h>
#include <
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值