VC++ 的MFC對時間的處理函數類,主要有兩對:CTime和CTimeSpan與COleDateTime和COleDateTimeSpan。筆者在使用中,發現其在早期的VC++6.0版本中存在時間計算bug,如:計算2095年與2020年的時間間隔為負值,即 -1928125696秒,使得時光倒流。本文在此與大家分享微軟VC++6.0 在使用CTime和CTimeSpan與COleDateTime和COleDateTimeSpan時出現的這個最大Bug。
1、先介紹VC++6.0的時間bug之一:用CTime類表示時間只能到2039年
CTime和CTimeSpan主要封裝了ANSI time_t和關於time_t的Run-Time庫的主要函數。CTime裡面使用的成員變量是time_t類型,該類型是個long型:
#ifndef _TIME_T_DEFINED
typedef long time_t; /* time value */
#define _TIME_T_DEFINED /* avoid multiple def's of time_t */
#endif
由於long類型的原因,所以該類只能處理2的32次方16T的時間即:4294967296秒(16進制1 00 00 00 00H,long int是4個字節,32位)
約69年的數據,所以用CTime只能處理1970年到2039年的日期。
而在VS2010中,定義為64位長度的時間值:
#ifndef _TIME64_T_DEFINED
typedef __int64 __time64_t; /* 64-bit time value */
#define _TIME64_T_DEFINED
#endif
在VS2010中,定義為2的64次方秒,即16*1TB*1TB的大小,高枕無憂了。
2、用COleDateTimeSpan類計算時間間隔(單位為秒)
MFC同時提供了COleDateTime和COleDateTimeSpan類,使用該兩個類完全可以代替CTime和CTimeSpan,COleDateTime和COleDateTimeSpan類所使用的成員變量是DATE類型,該類型是個double類型,而且使用的單位是日,所以可以處理到9999年12月31日的日期時間。
COleDateTimeSpan類是用於對COleDateTime類的兩個時間的時間間隔的計算,COleDateTimeSpan類使用的成員變量COleDateTimeSpan::m_span是一個double類型是用於記錄兩個COleDateTime::m_dt的時間差,例如:
COleDateTime t1(2020,1,1,0,0,0);
COleDateTime t2(2040,1,1,0,0,0);
COleDateTimeSpan ts=t2-t1;
CString str;
str.Format("%f",ts.GetTotalSeconds());
AfxMessageBox(str);
此程序段在VC++6.0及VS2010上計算,結果一致,相當精準,如下圖:
3、這是不是意味著COleDateTimeSpan類的任何成員函數,執行程序都不會有問題呢?答案是否定的!
我們先分別在VC++6.0及VS2010上運行同一段程序,用2、的方法計算2095年與2020年的時間間隔,看看結果如何。
先在vc++6.0上做一按鈕控件,執行如下程序段:
這段程序在VC++6.0上進行運算輸出結果是:-1928125696秒,這個結果明顯是一個溢出。時光倒流!
計算結果截圖如下:
同樣一段程序,在VS2010上顯示程序如下:
程序文本與VC++6.0的完全一樣,但在VC2010上進行運算輸出結果是:2366861600秒
75年,完美,非常正確的一個計算結果!
計算結果截圖如下:
這到底是什麼原因呢?
4、答案在COleDateTimeSpan類的成員GetTotalSeconds( )的原型定義上
VC++ 6.0 在使用COleDateTimeSpan類的GetTotalSeconds( )時,函數原型為:
double GetTotalSeconds( ) const;
返回值是double類型,但是,double GetTotalSeconds( ) const
在VC++ 6.0的MFC內部的原形定義卻如下:
_AFXDISP_INLINE double COleDateTimeSpan::GetTotalSeconds() const
{
ASSERT(GetStatus() == valid);
long lReturns = (long)(m_span * 24 * 60 * 60 + AFX_OLE_DATETIME_HALFSECOND);
return lReturns;
}
哈哈!原因就出在返回的LONG上!與“1、時間bug之一”一樣,計算大於69年的時間(單位為秒)間隔,時間差為負值!這是VC++ 6.0 MFC的又一個時間Bug!
幸好,微軟在VS2010中作了改正。
在VS2010的時間定義文件 ATLCOMTIME.INL中,原型定義如下:
ATLCOMTIME_INLINE double COleDateTimeSpan::GetTotalSeconds() const throw()
{
ATLASSERT(GetStatus() == valid);
return (double)LONGLONG((m_span + (m_span < 0 ?
-OLE_DATETIME_HALFSECOND : OLE_DATETIME_HALFSECOND)) * (24 * 60 * 60));
}
很明顯,在VS 2010中返回為double類型,double的定義為8個字節,是LONG的平方,可表示2的64次方。而在VC++ 6.0中,返回還是為long類。所以在使用函數GetTotalSeconds( )的時候,VC++ 6.0計算兩個時間的間隔大於69年時就會溢出,VS 2010卻安然無恙!