http://www./jiaocheng/20120910/1995886.html 2012 Linux下系统时间函数、DST等相关问题总结1. 内核中时间的基本类型:在Linux内核中,常见的时间类型有以下两种 Linux下系统时间函数、DST等相关问题总结 1. 内核中时间的基本类型: 在Linux内核中,常见的时间类型有以下两种:系统时间(system time)和实时时间(real time),其实,方便理解,可以将二者分别认为是相对时间和绝对时间,同时它们分别对应于内核中的两个全局变量值:jiffies和xtime。 xtime: xtime值是从cmos电路中取得的时间,一般是从某个历史时刻(1970年1月1日0时0分)开始到现在的时间,其实也就是我们操作系统上面所显示的时间,它的精度是微秒。 jiffies:jiffies是记录从电脑开机到现在总共的时钟中断次数(拍数),它的值取决于系统的频率,单位是HZ,其倒数即表示一秒钟中断所产生的次数,在Linux 2.5内核版本之后将HZ从100提高到1000MHZ,它的精度也就是10毫秒。 根据对上面两个全局变量值的介绍,大提升应该了解到Linux系统中系统时间与实时时间之间的区别,前者表示的是从电脑开机到现在的时间,可以通过全局变量jiffies值换算而来;而实时时间则是指我们日常生活中的日期时间,它跟UTC有着密切关系,这些将在后面章节做介绍。
2. Linux time API中常见的时间结构: (1)time_t:它是一个长整型数据,用来表示从1970年之后到现在的秒数。一般通过time函数获取。 (2)timeval结构:通过gettimeofday函数获取。 struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */ }; (3)timezone结构:通过gettimeofday函数获取。 struct timezone { int tz_minuteswest; /* 和Greewich时间差了多少分钟*/ int tz_dsttime; /*DST types*/ }; 【引申】常见的DST类型如下: #define DST_NONE 0 /* not on dst */ #define DST_USA 1 /* USA style dst */ #define DST_AUST 2 /* Australian style dst */ #define DST_WET 3 /* Western European dst */ #define DST_MET 4 /* Middle European dst */ #define DST_EET 5 /* Eastern European dst */ #define DST_CAN 6 /* Canada */ (4)timespec结构:通过clock_gettime函数获取。 struct timespec { time_t tv_sec; /* seconds */ long tv_nsec; /* nanoseconds */ }; (5)tm结构:通常由gmtime, localtime, mktime等函数返回。 struct tm { /* * the number of seconds after the minute,normally in the range * 0 to 59, but can be up to 60 to allow forleap seconds */ int tm_sec; /* the number of minutes after the hour, in the range 0 to 59*/ int tm_min; /* the number of hours past midnight, in the range 0 to 23 */ int tm_hour; /* the day of the month, in the range 1 to 31*/ int tm_mday; /* the number of months since January, in the range 0 to 11 */ int tm_mon; /* thenumber of years since 1900 */ long tm_year; /* the number of days since Sunday, in the range 0 to 6 */ int tm_wday; /* the number of days since January 1, in the range 0 to 365 */ int tm_yday; };
3. 常见的时间系统函数: (1) time: #include <time.h> time_t time(time_t *t) 若函数的参数为NULL,则返回从1970年1月1日0时0分0秒到现在(系统时间)所经过的秒;若参数非空,则将返回的值存在由指针t所指代的内存中。 (2) gettimeofday: #include <sys/time.h> int gettimeofday(structtimeval *tv ,struct timezone *tz ) 此函数可以获取两方面的时间信息,一个是可以获取到从1970年1月1日0时0分0秒到现在(系统时间)所经过的微秒,精度相比time函数精度有所提升;另外还可以获取到系统的时区信息。 【说明】 ◆ gettimeofday函数成功返回0;否则返回-1,错误存储在errno中。 ◆ tz_minuteswest值的确定问题:它表示的是与GTM之间相差的分钟数,其值应该为GMT(GMT +0)减去本地 时区对应的时间所得到的值,以EDT(GMT -4)为例,其值为240分钟。 ◆ 在实际开发中,gettimeofday中的tz参数实际很少使用,因为各种原因,一直未能实现(所获取出来的值恒为 0),因此,通常将此处直接写成NULL。 ◆ 对于gettimeofday函数的效率以及内部实现(系统调用实现),可参考 http://blog.csdn.net/russell_tao/article/details/7185588中的阐述。 ◆ 与gettimeofday函数相对应的是settimeofday,它可以设置实时时间RTC。但之前必须要具有root权限。 (3) gmtime,localtime and mktime: struct tm*gmtime(const time_t *timep) struct tm *localtime(const time_t *timep) time_tmktime(struct tm*tm) 以上三个函数实现了time_t与tm结构的互换。前两者将time_t结构转换成tm结构,mktime则正好相反。 【说明】gmtime与localtime之间的区别: 二者均可以将time_t结构的时间值转化成真实世界所使用的日期时间表示方法(tm结构),但是,前者返回的时间值未作时区的转换,即返回的是UTC时间;而localtime函数则返回的经过了时区转换的时间值,所获取到的值才是本地的真实时间。例如,在Linux系统中运行date命令,它显示的是经过时区转换之后的时间值(通过localtime获取),而若运行“date-u”则能显示未经过时区转换的UTC时间(通过gmtime获取)。
(3) strftime: #include <time.h> size_tstrftime (char *s,sizetsize, const char *format,const struct tm *brokentime) 此函数的功能是将由brokentime指针所指的时间按照format指针所指的格式输出到由s指针所指向的存储空间中,其中size是指存储空间的最大值。若返回0,则表明出现错误,所写进存储空间的结果是未定义的,若为真,则返回的是写进存储空间的字符数。
(4) clock_gettime: #include <time.h> intclock_gettime(clockid_tclk_id,struct timespec *tp); 此函数的功能是用来获取不同类型计时时钟的时间,其类型由clockid_t指定,常见的有: CLOCK_REALTIME(与实时时间对应) CLOCK_MONOTONIC(与系统时间对应) 【说明】 ◆ clock_gettime函数能将所获得的时间值精确到纳秒级别; ◆ 函数运行成功则返回0,否则返回-1,并将错误存在errno中; ◆ 除上面的两个时钟类型之外,还有以下两种类型: CLOCK_PROCESS_CPUTIME_ID和CLOCK_THREAD_CPUTIME_ID 但是这两种时钟类型一般出现在多处理机系统(SMP)中,值得注意的是,在老版本的Linux系统中可能会出现CPU时间之间不一致的现象,这是因为不同的CPU之间没有保证时间一致性措施,导致CPU时间之间出现偏移量,在2.6.18版本之后解决了这方面的问题,使得系统启动后不同的CPU之间具有相同的时间基准点。 ◆CLOCK_REALTIME时间可以通过settime或者settimeofday函数进行修改,或者通过NTP周期性 地纠正,此时需要用到adjtimex函数;CLOCK_MONOTONIC时间则不能通过settime或者settimeofday函数进行修改,但是同样可以通过NTP进行调整,此时同样需要用到adjtimex函数。 ◆对比两种时钟类型,若要在实际开发中要统计某个事件的时间,则最好是使用CLOCK_MONOTONIC,因为CLOCK_REALTIME被影响的因素太多,如手动修改,时区变化等等。
4. DST以及相关的系统函数: (1)UTC、GMT与DST 目前世界上常见的计时方式主要有:太阳时(MT)和原子时。GMT(格林尼治时间)的正午是指当太阳横穿格林尼治子午线时的时间,由于地球的自转呈现不规则性,并且正在缓慢减速,因此格林威治时间目前已经不再作为标准时间使用,取而代之的是协调时间时(UTC),它是由原子钟提供,它是基于标准的GMT提供的准确时间,若在不需要精确到秒的前提下,通常也将GMT与UTC视作等同。 DST(daylight saving time)也称为夏令时,它是以节约能源为目的而人为规定的一种制度,它规定某段时间作为夏令时间,并在标准时间的基础上提前多长时间(通常是一个小时),同时DST还规定了规定生效的起始时间和末尾时间,详细规则会在tzset函数中介绍,值得注意的是目前只是部分国家实施了夏令时制度。 标准时间是相对于UTC/GMT时间而言的,它在UTC/GMT之上增加了时区信息,比如中国标准时间是GMT+8,即在UTC时间上增加8个小时。 (2) 系统时间、标准时间以及UTC时间之间的关系: 这节主要探讨在具体项目实现过程中,如何处理系统时间、标准时间以及UTC时间之间的关系,其中系统时间可以通过前面的系统函数获取到,它可能正处于夏令时间区域,下面这个图可以清晰地阐述三者之间的关系: 我们以localtime函数获取到本地系统时间为例,演示如何将其转换成UTC时间,前面已经说过,localtime所获取到的时间已经包含了时区信息,但是之前我们必须要确认目前的这个时间是否处于夏令时区域之内,若是,则还需要经过A阶段(去掉DST偏移量,通常是一个小时),若不是,只需要经历第二个阶段B,即去时区,最后转化成UTC,当然这两个阶段并没有严格的先后顺序。反过来,在具体实现中,还经常出现将UTC时间转化成本地时间的情况,比如NTP就是基于这样的原理,它从NTP server端获取统一的UTC时间,然后需要经过C(加时区)和D(加DST,如果存在或正好处于夏令时区域范围之内的话)两个阶段将其转化成本地系统时间。 下面主要阐述第一种情况(本地系统时间——>UTC)是如何具体实现的。当然前提是我们要知道目前所在的时区,这是一切的根本。在此之前,值得说明的是,一般来讲,时区是一个固定的信息,难以想象一个国家或地区去改变时区所带来的后果,但是DST因为是人为规定的,因此可能存在着修改的情况,基于这个事实,在具体实现中,时区信息可以存储在本地,而DST信息既可以静态存储在本地,也可以通过相关的server动态获取到。我们以静态存储的方式为例来讲解具体是如何实现去时区,去DST。 下面这个结构体存储了跟时区相关的位移量(offset)以及是否存在DST等信息,根据所在的时区信息,很容易找到系统时间与UTC时间之间的时区偏移,另外根据rule是否为-1来确定此时区是否实施了夏令时,若为-1,表明这个时区地已经实现了夏令时,则还需要经过去DST阶段,否则只需要经过去时区就可以得到UTC时间。 struct zone zones[N_ZONES] = { /* offset rules */ { -43200, -1 }, /* (GMT-12:00)International Date Line West */ { -39600, -1 }, /* (GMT-11:00) Midway Island,Samoa */ { -36000, -1 }, /* (GMT-10:00) Hawaii */ { -32400, 0 }, /* (GMT-09:00) Alaska */ { -28800, 0 }, /* (GMT-08:00) Pacific Time, Tijuana */ { -25200, -1 }, /* (GMT-07:00) Arizona, Mazatlan*/ { -25200, 13 }, /* (GMT-07:00) Chihuahua, La Paz*/ { -25200, 0 }, /* (GMT-07:00) Mountain Time */ { -21600, 0 }, /* (GMT-06:00) Central America */ { -21600, 0 }, /* (GMT-06:00) Central Time */ { -21600, 13 }, /* (GMT-06:00) Guadalajara, MexicoCity, Monterrey*/ { -21600, -1 }, /* (GMT-06:00) Saskatchewan */ { -18000, -1 }, /* (GMT-05:00) Bogota, Lima, Quito */ { -18000, 0 }, /* (GMT-05:00) Eastern Time */ { -18000, -1 }, /* (GMT-05:00) Indiana */ { -14400, 0 }, /* (GMT-04:00) Atlantic Time */ {-14400, -1 }, /* (GMT-04:00) Caracas, La Paz */ { -14400, 2 }, /* (GMT-04:00) Santiago */ { -12600, 0 }, /* (GMT-03:30) Newfoundland */ { -10800, 14 }, /* (GMT-03:00) Brasilia */ { -10800, -1 }, /* (GMT-03:00) Buenos Aires, Georgetown*/ { -10800, -1 }, /* (GMT-03:00) Greenland */ { -7200, -1 }, /* (GMT-02:00) Mid-Atlantic */ { -3600, 1 }, /* (GMT-01:00) Azores */ { -3600, -1 }, /* (GMT-01:00) Cape Verde Is. */ { 0, -1 }, /* (GMT) Casablanca, Monrovia */ { 0, 1 }, /* (GMT) Greenwich MeanTime: Dublin, Edinburgh,Lisbon, London*/ { 3600, 1 }, /* (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna { 3600, 1 }, /* (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague */ { 3600, 1 }, /* (GMT+01:00) Brussels, Copenhagen, Madrid, Paris*/ { 3600, 1 }, /* (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb*/ { 3600, -1 }, /* (GMT+01:00) West Central Africa*/ { 7200, 1 }, /* (GMT+02:00) Athens, Istanbul, Minsk */ { 7200, 1 }, /* (GMT+02:00) Bucharest */ { 7200, 4 }, /* (GMT+02:00) Cairo */ { 7200, -1 }, /* (GMT+02:00) Harare, Pretoria */ { 7200, 1 }, /* (GMT+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius */ { 7200, 5 }, /* (GMT+02:00) Jerusalem */ { 10800, 6 }, /* (GMT+03:00) Baghdad */ { 10800, -1 }, /* (GMT+03:00) Kuwait,Riyadh */ { 10800, 7 }, /* (GMT+03:00) Moscow, St. Petersburg, Volgograd */ { 10800, -1 }, /* (GMT+03:00) Nairobi*/ { 12600, 8 }, /* (GMT+03:30) Tehran */ { 14400, -1 }, /* (GMT+04:00) Abu Dhabi, Muscat */ { 14400, 9 }, /* (GMT+04:00) Baku, Tbilisi, Yerevan */ { 16200, -1 }, /* (GMT+04:30) Kabul*/ { 18000, 7 }, /* (GMT+05:00)Ekaterinburg */ { 18000, -1 }, /* (GMT+05:00) Islamabad, Karachi, Tashkent*/ { 19800, -1 }, /* (GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi */ { 20700, -1 }, /* (GMT+05:45) Kathmandu*/ { 21600, 12 }, /* (GMT+06:00) Almaty, Novosibirsk */ { 21600, -1 }, /* (GMT+06:00) Astana, Dhaka*/ { 21600, -1 }, /* (GMT+06:00) Sri Jayawardenepura */ { 23400, -1 }, /* (GMT+06:30) Rangoon */ { 25200, -1 }, /* (GMT+07:00) Bangkok, Hanoi, Jakarta*/ { 25200, 7 }, /* (GMT+07:00) Krasnoyarsk */ { 28800, -1 }, /* (GMT+08:00) Beijing,Chongquing, Hong Kong, Urumqi*/ { 28800, -1 }, /* (GMT+08:00) Irkutsk,Ulaan Bataar */ { 28800, -1 }, /* (GMT+08:00) Kuala Lumpur, Singapore*/ { 28800, -1 }, /* (GMT+08:00) Perth*/ { 28800, -1 }, /* (GMT+08:00) Taipei*/ { 32400, -1 }, /* (GMT+09:00) Osaka, Sapporo, Tokyo*/ { 32400, -1 }, /* (GMT+09:00) Seoul*/ { 32400, 7 }, /* (GMT+09:00) Yakutsk */ { 34200, 3 }, /* (GMT+09:30) Adelaide */ { 34200, -1 }, /* (GMT+09:30) Darwin*/ { 36000, -1 }, /* (GMT+10:00) Brisbane*/ { 36000, 3 }, /* (GMT+10:00) Canberra, Melbourne, Sydney*/ { 36000, -1 }, /* (GMT+10:00) Guam, Port Moresby */ { 36000, 10 }, /* (GMT+10:00) Hobart*/ { 36000, 7 }, /* (GMT+10:00) Vladivostok */ { 39600, -1 }, /* (GMT+11:00) Magadan */ { 39600, 7 }, /* (GMT+11:00)Solomon Is., New Caledonia*/ { 43200, 11 }, /* (GMT+12:00) Auckland, Wellington */ { 43200, -1 }, /* (GMT+12:00) Fiji,Kamchatka, Marshall Is. */ { 43200, -1 }, /* (GMT+12:00) NZ */ }; 那么又如何去掉DST,即找到系统时间与标准时间之间的DST偏移量呢?在此之前需要了解到DST的规则问题,如规则格式、规则数据等等。 DST规则规定了实施夏令时的起始时间以及结束时间,如澳大利亚的是:从4月的第一个星期天的凌晨3点到10月的第一个星期天的凌晨2点,全世界DST可参考www.worldtimezone.com/daylight.html。下面主要阐述如何判断目前的时间是否包含有夏令时。 rpytime(rule1,year) < (gm_time + zone->z_gmtoff))< rpytime(rule2,year) 上面的式子中gm_time是本地系统时间(注意是通过localtime获取,没有加入时区,单位为秒),z_gmtoff是指制定时区的偏移量,这样式子中间代表就是标准时间;式子中rule1,rule2分别对应于DST规则中的两个界点,并利用rpytime函数计算出从1970年以来的时间总长(以秒为单位),若上面的式子成立,表明存在DST,那是因为DST使得在标准时间之上提前了1小时。
|
|