Linux(程序设计):20---时间处理(timespec、ctime、difftime、gmtime、clock_gettime、localtime、strftime、strptime)

格林尼治时间

  • 所有的UNIX系统都使用同一个时间和日期的起点:格林尼治时间(GMT)1970年1月1 日午夜(0点)。这是“UNIX纪元的起点”,Linux也不例外。Linux系统中所有的时间都以从那时起经过的秒数来衡量

 时间转换关系

TZ环境变量

  • 在上图中,虚线表示的3个函数(localtime、mktime、strftime)受到环境变量TZ的影响
    • 如果定义了TZ:则这3个函数使用其值替代系统默认时区
    • 如果TZ定义为空串(及TZ=):则使用协调统一时间UTC
  • TZ的值类似于TZ=EST5EDT,但是POSIX.1允许更详细说明,有关TZ变量的详细信息课自行查询

一、time_t数据类型

  • 时间通过一个预定义的类型time_t来处理,我们称time_t表示的时间成为日历时间
  • 这是一个大到能够容纳以秒计算的日期和时间的整数类型,它代表从格林尼治时间开始截止到目前为止的时间秒数
  • 在Linux系统中,它是一个长整型,与处理时间值的函数一起定义在头文件time.h中

二、struct  timespec结构体

struct timespec
{
    ...
    time_t tv_sec;
    long tv_nsec;
    ...
};
  • timespec结构体按照秒和纳秒来定义时间
  • 结构体中至少包含以上两个成员:
    • tv_sec:秒数
    • tv_nsec:纳秒
  • timespec结构体提供了更高精度的时间戳

三、time函数

#include <time.h>
time_t time(time_t *t);

//返回值:成功返回时间值;出错返回-1
  • 调用time函数得到底层的时间值,它返回的是从格林尼治时间开始至今的秒数
  • 如果参数t不是一个空指针,time函数还会把返回值写入t指针指向的位置

演示案例

//envtime.c

#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
    int i;
    time_t the_time;

    for(i=1;i<=10;i++){
        the_time=time((time_t *)0);
        printf("The time is %ld\n",the_time);
        sleep(2);
    }

    exit(EXIT_SUCCESS);
}
  •  演示效果:

四、difftime函数

#include <time.h>

double difftime(time_t time1, time_t time0);
  • 以从1970年开始计算的秒数来表示时间和日期,对测算某些事情持续的时间是很有用的。你可以 把它考虑为简单地把两次调用time得到的值相减。然而ISO/ANSI C标准委员会经过审议,并没有规定用time_t类型来测量任意时间之间的秒数,他们发明了一个函数difftime,该函数用来计算两个time_t值之间的秒数并以double类型返回它 
  • difftime函数计算两个时间值之间的差,并将time1-time0的值作为浮点数返回。对Linux来说, time函数的返回值是一个易于处理的秒数,但考虑到最大限度的可移植性,你最好使用difftime

演示案例

//difftime.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{

    time_t time1;
    time_t time2;

    time1=time((time_t*)NULL);
    sleep(2);
    time2=time((time_t*)NULL);
    printf("time1:%ld\ntime2:%ld\n",time1,time2);
    printf("time2 - time1 = %f\n",difftime(time2,time1));
    
    exit(EXIT_SUCCESS);
}
  • 演示效果:

五、时钟接口函数(clock_gettime、clock_getres、clock_settime)

  • POSIX1.1的实时扩展增加了对多个系统时间的支持。在Single UNIX Specification V4中,控制这样时钟的接口从可选组被移至基本组
  • 时钟通过clockid_t类型进行表示
  • 下面给出了标准值

clock_gettime函数

  • 该函数可用于获取指定时钟的时间,将获取的时钟时间存放在参数2中
  • 当时钟ID设置为CLOCK_REALTIME时,clock_gettime函数提供了与time函数类似的功能,不过在系统支持高精度时间值的情况下,clock_gettime可能比time函数得到更高精度的时间值

clock_getres函数

  • 该函数把参数把参数tsp指向的timespec结构初始化为与clock_id参数对应的时钟精度
  • 例如:如果精度为1毫秒,则timespec结构体的tv_sec字段就被设置为0,tv_nsec字段就被设置为1000000

clock_settime函数

  • 要对特定的时钟设置时间,可以调用clock_settime函数
  • 我们需要适当的特权来更改时钟值,但是有些时钟是不能修改的

六、gettimeofday

  • SUSv4指定该函数已被弃用了,但是一些程序仍然使用这个函数,因为与time函数相比,该函数提供了更高的精度(因为其他时间保存在struct timeval结构体中)
  • 该函数与time函数类似,其将从格林尼治时间开始到目前为止的时间差存放到参数1所指的结构体中
  • 参数:
    • 参数1:保存从格林尼治时间开始到目前为止的时间差
    • 参数2:设置为NULL。如果设置为其他值将产生不确定的结果(某些平台支持用该参数说明时区,但是这完全依实现而定,Single UNIX Specification并没有定)

七、gmtime函数、localtime函数

#include <time.h>

struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);

//返回值:成功返回tm结构指针;若出错返回NULL
  • 这两个函数都是将日历时间转换为分解的时间,并将分解后的内容放入到一个tm结构体中 

gmtime

  • 为了提供(对人类)更有意义的时间和日期,需要把时间值转换为可读的时间和日期,gmtime函数把底层时间值分解为一个结构
  • 注意:该函数返回的stucr tm结构体中的时分秒是针对于格林尼治标准时间(GMT)之内地区的时分秒,所以下面的演示案例中打印的时分秒与当地的时间不一致(程序运行在中国),如果想要回去当地的时分秒,需要使用下面的localtime函数

localtime

  • 上面介绍的时间都是以格林尼治标准时间(GMT)为标准返回的时间。如果在格林尼治标准时间(GMT)之外的时区使用gmtime函数,或者所在的地方像本例中那样采用了夏令时,你会发现时间(可能还有日期)是不对的。如果要看当地时间,你需要使用localtime函数
  • 该函数收到TZ环境变量的影响,具体见文章最上方介绍
  • 功能:localtime函数和gmtime一样,除了它返回的结构中包含的值已根据当地时区和是否采用夏令时做了调整

struct tm结构体 

struct tm {
    int tm_sec;         /* 秒:0~60 */
    int tm_min;         /* 分:0~59 */
    int tm_hour;        /* 小时:0~23 */
    int tm_mday;        /* 月份中的日期:1~31 */
    int tm_mon;         /* 月份:0~11(一月份为0) */
    int tm_year;        /* 从1900年开始计算的年份 */
    int tm_wday;        /* 星期几:0~6(周日为0) */
    int tm_yday;        /* 年份中的日期:0~365 */
    int tm_isdst;       /* 是否为夏令时 */
};
  • tm_sec:其范围超过59是因为其允许临时表示润秒(Single UNIX Specification的以前版本允许双润秒,所以该字段的范围为0~61,但是UTC的正式定义不允许双润秒,所以现在该字段的范围为0~60)
  • 除了月日字段,其他字段都是以0开始
  • tm_isdest:如果夏令时生效,则该字段值为正;如果为非夏令时时间,则该字段值为0;如果此信息不可用,则其值为负

演示案例(gmtime)

//gmtime.c

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    struct tm *tm_ptr;
    time_t the_time;

    (void)time(&the_time);
    tm_ptr=gmtime(&the_time);

    printf("Raw time is %ld\n",the_time);
    printf("gmtime gives:\n");
    printf("date: %02d/%02d/%02d\n",
        tm_ptr->tm_year,tm_ptr->tm_mon+1,tm_ptr->tm_mday);//打印年、月、日
    printf("time: %02d:%02d:%02d\n",
        tm_ptr->tm_hour,tm_ptr->tm_min,tm_ptr->tm_sec);//打印时、分、秒

    exit(EXIT_SUCCESS);
}

  • 演示效果:
    • 严格来说,你不应该用这种方法打印原 始时间值,因为我们并不能保证它在所有系统上都是long类型的值
    • 我们在运行gmtime程序后立即运行date命令以比较它们的输出
    • 由于当地地区(中国)不属于格林尼治标准时间(GMT)之内的时区,所以下面打印的“time”与date命令输出的不同,如果想要获取当地的时分秒,需要使用下面的locatime函数

演示案例(localtime)

//localtime.c

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    struct tm *tm_ptr;
    time_t the_time;

    (void)time(&the_time);
    tm_ptr=localtime(&the_time);

    printf("Raw time is %ld\n",the_time);
    printf("gmtime gives:\n");
    printf("date: %02d/%02d/%02d\n",
        tm_ptr->tm_year,tm_ptr->tm_mon+1,tm_ptr->tm_mday);
    printf("time: %02d:%02d:%02d\n",
        tm_ptr->tm_hour,tm_ptr->tm_min,tm_ptr->tm_sec);

    exit(EXIT_SUCCESS);
}
  •  如果把上面gmtime.c程序中的gmtime换成localtime,再编译运行一次,你就会看到正确的时间和日期了

八、mktime函数

#include <time.h>

time_t mktime(struct tm *tm);

//返回值:成功返回日历时间;出错返回-1
  • 该函数把tm结构再转换为原始的time_t时间值
  • 该函数收到TZ环境变量的影响,具体见文章最上方介绍

演示案例

//mktime.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
    time_t the_time;
    struct tm *tm_ptr;
    
    the_time=time((time_t*)0);
    tm_ptr=localtime(&the_time);
    
    printf("%ld\n",the_time);
    printf("%ld\n",mktime(tm_ptr));
    return 0;
}

九、asctime函数、ctime函数

#include <time.h>

char *asctime(const struct tm *tm);
char *ctime(const time_t *timep);
  • 为了得到更“友好”的时间和日期表示,像date命令输出特定的格式,你可以使用asctime函数和ctime函数
  • asctime函数和ctime函数的返回值字符串格式都是相同的,只是两个函数的参数类型不同罢了
  • 备注:这两个函数现在已经被标记为弃用了,因为它们易受到缓冲区溢出问题的影响

asctime

  • asctime函数返回一个字符串,它表示由tm结构timeptr所给出的时间和日期。字符串总是以26个字符为固定格式
//asctime.c

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    struct tm *tm_ptr;
    time_t the_time;

    (void)time(&the_time);
    tm_ptr=gmtime(&the_time);

    printf("The date is:%s\n",asctime(tm_ptr));
    exit(EXIT_SUCCESS);
}


ctime

  • 它以原始时间值为参数,并将它转换为一个更易读的本地时间
  • ctime函数等效于调用下面这个函数:

//ctime.c

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    time_t timeval;

    (void)time(&timeval);
    printf("The date is:%s\n",ctime(&timeval));
    exit(EXIT_SUCCESS);
}
  •  ctime.c程序调用time函数得到底层时间值,让ctime做所有的艰巨工作,把时间值转换成可读的字符串,然后打印它

十、strftime、strftime_l、strptime

strftime

 #include <time.h>

size_t strftime(char *s, size_t max, const char *format,const struct tm *tm);
  • 功能: 这两个很像是一个针对时间和日期的sprintf函数,工作方式也很类似,用于格式化打印时间日期
  • strftime函数收到TZ环境变量的影响,具体见文章最上方介绍
  • 参数:
    • s:用来保存格式化的结果
    • max:对应于参数1所指的数组长度
    • format:控制时间值的格式,如同printf函数一样,转换说明的形式是百分号之后跟一个特定字符(见下表)
    • tm:要格式化的时间值,指向于一个struct tm结构体
    • locale:允许调用者将区域指定为参数(仅strftime_l函数拥有)
  • 返回值:如果s的长度足以存放格式化结果及一个null终止符,则这两个函数返回在s中存放的字符数(不包括null终止符);否则返回0
  • 转换控制符:与printf一样, 它包含将被传给字符串的普通字符和用于格式化时间和日期元素的转换控制符。有一些需要略作解释的控制符如下
    • %U:是相应日期在该年中所属周数,包含该年中第一个星期日的周是第一周
    • %W:也是相应日期在该年中所属的周数,不同的是包含第一个星期一的周围第一周
    • %V:与上面两者有较大的区别。如果包含了1月1日的那一周包含了新一年的4天或更多天,那么该周是一年中的第一周;否则该周被认为是上一年的最后一周

  • date命令输出的普通日期就相当于strftime格式化字符串中的:
%a %b %d %H:%M:%S %Y

strptime

#define _XOPEN_SOURCE       /* See feature_test_macros(7) */
#include <time.h>

char *strptime(const char *s, const char *format, struct tm *tm);
  • 功能:为strptime函数是strftime的反过来版本,把字符串时间转换为分解时间
  • 参数:
    • s:缓冲区,接受参数2的日期格式化字符串
    • format:日期的格式化字符串
    • tm:将格式化的日期转换为保存到struct  tm结构体中
  • strptime函数转换说明符如下所示:与strftime函数的说明符稍有不同

  • format字符串的构建方式和strftime的format字符串完全一样。strptime在字符串扫描方面类 似于sscanf函数,也是查找可识别字段,并把它们写入对应的变量中。只是这里是根据format字符 串来填充tm结构的成员
  • 不过,strptime的转换控制符与strftime的相比,限制要稍微松一些,因 为strptime中的星期几和月份用缩写和全称都行,两者都匹配strptime中的%a控制符,此外, strftime对小于10的数字总以0开头,而strptime则把它看作是可选的
  • strptime返回一个指针,指向转换过程处理的最后一个字符后面的那个字符。如果碰到不能转换 的字符,转换过程就在该处停下来。调用程序需要检查是否已从传递的字符串中读入了足够多的数据, 以确保tm结构中写入了有意义的值
  • _XOPEN_SOURCE宏:如果不加这个宏,在编译程序时可以能警告。这是因为GNU库在默认情况下并未 声明strptime函数。要解决这个问题,你需要明确请求使用X/Open的标准功能,也就是加上这个宏

演示案例

//strftime.c

#define _XOPEN_SOURCE /* glibc2 needs this for strptime */
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    struct tm *tm_ptr, timestruct;
    time_t the_time;
    char buf[256];
    char *result;

    (void) time(&the_time);
    tm_ptr = localtime(&the_time);
    strftime(buf, 256, "%A %d %B, %I:%S %p", tm_ptr);

    printf("strftime gives: %s\n", buf);

    strcpy(buf,"Thu 26 July 2007, 17:53 will do fine");

    printf("calling strptime with: %s\n", buf);
    tm_ptr = &timestruct;

    result = strptime(buf,"%a %d %b %Y, %R", tm_ptr);
    printf("strptime consumed up to: %s\n", result);

    printf("strptime gives:\n");
    printf("date: %02d/%02d/%02d\n", 
        tm_ptr->tm_year % 100, tm_ptr->tm_mon+1, tm_ptr->tm_mday);
    printf("time: %02d:%02d\n",
        tm_ptr->tm_hour, tm_ptr->tm_min);
    exit(0);
}
  • 程序设计: strftime程序通过调用time和localtime得到当前的本地时间。然后,它通过调用带有合适的格 式参数的strftime将时间转换成可读的格式。为演示strptime的用法,程序构建了一个包含日期和 时间的字符串,然后调用strptime将原始时间和日期值提取并打印出来。转换控制符%R是strptime 中对%H:%M的缩写形式

  • 注意:要成功地扫描日期,strptime需要一个准确的格式字符串,这一点非常重要。一般来说, 该函数不会准确扫描读自用户的日期,除非用户输入的格式非常严格
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董哥的黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值