zoukankan      html  css  js  c++  java
  • 每天进步一点点——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* tvstruct 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到如今的秒数。这个数值很不便于我们直接解释,由于我们更easy接受的是MM-DD-YY HH:MIN:SS的形式。Linux中提供了简单的ctime()函数来处理time_t的转换
    #include <time.h>

    // Returns pointer to statically allocated string terminated by newline and 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 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 *outstrsize_t maxsizeconst char *formatconst 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 *strconst char *formatstruct 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


    版权声明:本文博主原创文章,博客,未经同意不得转载。

  • 相关阅读:
    Guns项目整体结构
    基于事件的NIO多线程服务器
    Reactor模式和NIO
    ConcurrentHashMap之实现细节
    C 语言的前世今生
    Netty系列之Netty高性能之道
    java synchronized详解
    生产者/消费者模式
    当spring 容器初始化完成后执行某个方法
    Linux系统管理员需要知道的16个服务器监控命令
  • 原文地址:https://www.cnblogs.com/hrhguanli/p/4868350.html
Copyright © 2011-2022 走看看