Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /www/wwwroot/fawdlstty.com/wp-content/plugins/wp-syntax/wp-syntax.php on line 383
时间在计算机中,通常以三种形式存储,三种方式各有各的特点。
1、Unix时间戳
这个东西算是时间表示的元老级结构了,因为出现的早,所以用的也最广泛。原理是,一个32位的数字,代表从1970年1月1日到现在的秒数。由于最高位代表符号位未被使用,所以可以表示的最长的时间点为2038年左右。目前所有Unix及类Unix操作系统都使用这种方式表示时间。著名的水果1970变砖BUG就是因为这种时间表示方式的固有特性所导致。这种时间表示方式还有一个非常大的问题就是,不能计算闰秒。闰秒的含义是,因为地球绕太阳自转的速度越来越慢,导致地球公转一圈的时间更久。现代的人们感受不出来,但如果时间线放长,并且不闰秒,那么可以预见,未来某一天的正午12点是晚上,凌晨12点是白天。所以,在某个时间点的闰秒尤为重要。闰秒通常在某个分钟的变化时,秒数记为57、58、59、60、0、1、2……,也就是说,多出来一个第60秒。闰秒虽好,统一地球公转,为太阳系历法做贡献,但每次闰秒通常会造成几千万美元的经济损失,原因为,使用了Unix时间戳这种计时方法的操作系统,并不能区分闰秒,导致操作系统时间与世界时间不同,然后在金融等领域,一秒钟多计算了什么什么,少计算了什么什么,导致结果不是实际想要的。这时候计算误差所导致的经济损失累积起来,就有这么严重了。另外,这种计时方式最多只能计算到2038年,之后就无能为力了。如果这些操作系统迟迟不更新原子计时方式,那么,等待它们的,只有,系统罢工了。因为以上两个问题太严重了,导致人们常常忽略了它的第三个问题:精度太差。最高精度就是秒了,但对于计算机来说,计算很多东西精度常常需要达到毫秒才够用,有的甚至需要微秒级精度,所以这些地方也用不了这种时间表示方式。
基于这种计时方式,出现了一个分支,使用64位进行计时,这就不存在年份限制这BUG了,不过这分支用的比较少。
2、DATE时间类型
网上也有将其称作VB时间类型或者浮点时间类型,使用一个浮点数,代表从1899年12月30日凌晨到目标时间的天数。比如,1900-01-01 00:00:00表示为:2.0;1900-01-01 06:00:00表示为:2.25。这种事件类型有一个好处就是精度高,另外它也能代表1899年前的时间,使用负数表示。这种时间类型还有一个分支,代表着从1900年1月1日起至今的天数,也就是说,上一个事件类型的值-2就成了这种时间类型的值。这种事件类型通常不能用累加来实现时间增量,所以不会出现闰秒的问题,另外精度也够高。有一个小缺憾是,这种时间类型由于是浮点数,计算稍显麻烦,另外也不能代表时间增量,总的来说,比Unix时间戳好多了。所以用的也比较多。Win32平台内部很多地方都使用的这种事件类型。
3、结构化时间类型
这种计时方式是最笨的,同时也是最有效的。只要一个时间结构,年月日时分秒分别用不同的整型数字存放,那么都属于这种时间类型。这种时间类型的分支也特别多,有的全部用int存储,有的月份用4位、日用5位来存储;有的月份范围为1~12,有的月份范围为0~11;有的精度只有秒,有的甚至可以存放微秒,由于分支特别多,所以在不同的分支上进行转换时,需要特别注意按照规定来。这种时间类型好处都有啥,谁说对了金坷垃送给他。
好咯。基本的时间存储方式说明白了,那么开始讲解代码。
1、计算时间差
有时候,需要计算一段代码的执行时间,比如多少多少毫秒,这时候如果用完整的时间类型通常大材小用了。代表时间差有两种便捷的方式,一种是使用Win32的GetTickCount函数,返回一个DWORD的时间。只需要在需要统计时间的代码前和后面分别调用它,相减,就是时间差的毫秒数。关于这个函数的实际含义,msdn上面说返回系统启动之后所经过的时间的毫秒数。我开始还以为是开机时间,然而经我测试后,结果换算了一下大约为10天左右,刚好这电脑有很长时间没用了,计算BIOS时间的电源也断了,大概在10天前开了一下,所以,我猜测,如果没有我那种完全放电的话,这个应该代表BIOS第一次加电时至今的毫秒数,如果经过断电,那就为上一次加电至今的毫秒数。示例代码如下:
1 2 3 4 5 6 7 8 | DWORD d = ::GetTickCount (); // 计时开始 // 一大串需要计时的代码,或者直接。。。 ::Sleep (1000); //暂停一秒钟 // 计时结束 d = ::GetTickCount () - d; // 这时候的d里面所存储的就是时间差的毫秒数 |
2、暂停一段时间
大型任务为避免持续占CPU时间,通常需要休息一段时间,让出CPU时间片给其他线程。暂停的方式除了上面Win32提供的Sleep函数外,还有一种是C++11所提供的,示例代码如下:
1 2 3 4 5 6 7 | #include <thread> #include <chrono> // ... std::this_thread::sleep_for (std::chrono::seconds (2)); // 暂停2秒 std::this_thread::sleep_for (std::chrono::milliseconds (10)); // 暂停10毫秒 |
3、C语言时间结构
C语言时间结构通常有两种类型,一种是time_t,一种是tm。time_t在32位或64位开发环境下,所代表的也分为两种不同大小的时间结构。定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #ifndef _TIME32_T_DEFINED typedef _W64 long __time32_t; /* 32-bit time value */ #define _TIME32_T_DEFINED #endif /* _TIME32_T_DEFINED */ #ifndef _TIME64_T_DEFINED typedef __int64 __time64_t; /* 64-bit time value */ #define _TIME64_T_DEFINED #endif /* _TIME64_T_DEFINED */ #ifndef _TIME_T_DEFINED #ifdef _USE_32BIT_TIME_T typedef __time32_t time_t; /* time value */ #else /* _USE_32BIT_TIME_T */ typedef __time64_t time_t; /* time value */ #endif /* _USE_32BIT_TIME_T */ #define _TIME_T_DEFINED /* avoid multiple def's of time_t */ #endif /* _TIME_T_DEFINED */ |
貌似。与目标环境配置无关,只要是在64位环境编译,就算目标生成类型为32位应用程序,那么time_t也是64位。
然后是tm这种结构,定义如下:
1 2 3 4 5 6 7 8 9 10 11 | struct tm { int tm_sec; /* seconds after the minute - [0,59] */ int tm_min; /* minutes after the hour - [0,59] */ int tm_hour; /* hours since midnight - [0,23] */ int tm_mday; /* day of the month - [1,31] */ int tm_mon; /* months since January - [0,11] */ int tm_year; /* years since 1900 */ int tm_wday; /* days since Sunday - [0,6] */ int tm_yday; /* days since January 1 - [0,365] */ int tm_isdst; /* daylight savings time flag */ }; |
不太节省内存空间哪,4位足够代表月份了,但足足用了一个int。也罢,现在计算机内存这么大,浪费这点也没什么。
以下代码示例基本用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // 获取当前时间,实际代表的值为本地时间 time_t t = time (NULL); // 将当前时间转为格林威治时间 //tm *t2 = gmtime (&t); // 将当前时间转为本地时间 tm *t2 = localtime (&t); // 输出当前时间 printf ("%4d-%02d-%02d %02d:%02d:%02d", t2->tm_year + 1900, t2->tm_mon + 1, t2->tm_mday, t2->tm_hour, t2->tm_min, t2->tm_sec); //// 下面这段代码效果同上一行代码完全一样,写法稍有不同 //char cBuf [64]; //strftime (cBuf, 64, "%Y-%m-%d %H:%M:%S", t2); //printf (cBuf); // 将tm时间结构转回time_t t = mktime (t2); |
然后是计算时间差。由于time_t精度为秒,所以无法计算秒以下的单位。示例代码如下:
1 2 3 4 5 6 | time_t t = time (NULL); std::this_thread::sleep_for (std::chrono::milliseconds (1234)); time_t t2 = time (NULL); // 计算时间差 printf ("%lf", difftime (t2, t)); |
结果为1.0000000,说明单位就是秒。但返回类型是双精度浮点型。估计这个小数没任何卵用。
4、C++11时间结构
使用前需包含头文件chrono。为了同C语言兼容,所以C++11时间也可以直接与time_t互转
1 2 3 4 5 6 7 8 | // 获取当前时间 std::chrono::system_clock::time_point tp = std::chrono::system_clock::now (); // C++11时间转C语言时间 time_t t = std::chrono::system_clock::to_time_t (tp); // C语言时间转C++11时间 tp = std::chrono::system_clock::from_time_t (t); |
除了必要的转换外,C++11一般格式化时间也是首先转为C语言时间结构体然后进行打印。然后同样是计算时差,这个时间结构稍微高级一些,精度可以达到纳秒(1e-9),示例代码如下:
1 2 3 4 5 6 7 | std::chrono::system_clock::time_point t = std::chrono::system_clock::now (); std::this_thread::sleep_for (std::chrono::seconds (2)); std::chrono::system_clock::time_point t2 = std::chrono::system_clock::now (); // 纳秒级精度 n = std::chrono::duration_cast <std::chrono::nanoseconds> (t2 - t).count (); // 将以上代码的 nanoseconds 替换成 seconds、milliseconds、microseconds 就分别代表着秒级精度、毫秒级精度、微秒级精度 |
另外c++11时间的输出如果需要带毫秒那稍微需要转一个弯,示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | std::string format_time () { char buf_time [32], buf_time2 [32]; buf_time [0] = buf_time2 [0] = '\0'; auto time_now = std::chrono::system_clock::now (); auto duration_in_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_now.time_since_epoch ()); auto ms_part = duration_in_ms - std::chrono::duration_cast<std::chrono::seconds>(duration_in_ms); time_t raw_time = std::chrono::system_clock::to_time_t (time_now); tm local_time_now; _localtime64_s (&local_time_now, &raw_time); strftime (buf_time2, sizeof (buf_time2), "%Y-%m-%d %H:%M:%S", &local_time_now); //char *xx = std::put_time (&local_time_now, "%Y-%m-%d %H:%M:%S"); _snprintf (buf_time, sizeof(buf_time), "%s.%03d", buf_time2, ms_part.count ()); return buf_time; } |
5、MFC/ATL时间结构
主要就是CTime和COleDateTime,后者使用前需包含ATLComTime.h头文件。这两者非常相似所以放在一起说;另外这个放在C++11之后不代表比C++11的更高级,我个人喜欢用标准的东西,但很多大型MFC项目用的基本都是这个,所以有必要说说。基本用法挺相似:
1 2 3 4 5 | CTime t = CTime::GetCurrentTime (); printf (t.Format ("%Y-%m-%d %H:%M:%S")); COleDateTime t2 = COleDateTime::GetCurrentTime (); printf (t2.Format ("%Y-%m-%d %H:%M:%S")); |
它俩构造函数比较相似,但COleDateTime功能稍强
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | CTime () throw (); CTime (__time64_t time) throw (); CTime (int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec, int nDST = -1); CTime (WORD wDosDate, WORD wDosTime, int nDST = -1); CTime (const SYSTEMTIME& st, int nDST = -1); CTime (const FILETIME& ft, int nDST = -1); CTime (const DBTIMESTAMP& dbts, int nDST = -1) throw (); COleDateTime () throw (); COleDateTime (const VARIANT& varSrc) throw (); COleDateTime (DATE dtSrc) throw (); COleDateTime (__time32_t timeSrc) throw (); COleDateTime (__time64_t timeSrc) throw (); COleDateTime (const SYSTEMTIME& systimeSrc) throw (); COleDateTime (const FILETIME& filetimeSrc) throw (); COleDateTime (int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec) throw (); COleDateTime (WORD wDosDate, WORD wDosTime) throw (); COleDateTime (const DBTIMESTAMP& dbts) throw (); |
可见CTime构造是相当受限制的,COleDateTime构造除了有以上内容外,还可以与VARIANT类型、DATE类型(浮点时间类型)互转,甚至time_t也区分了32位与64位。
虽然这俩功能还行但毕竟是M$大大的东西,推荐还是使用C/C++标准写法,兼容性更强,并且如果以后项目需要迁移至其他平台,这种写法也更方便,几乎不用修改代码。