VC++不為人所知的時間編程Bug


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上計算,結果一致,相當精準,如下圖:


VC++不為人所知的時間編程Bug


3、這是不是意味著COleDateTimeSpan類的任何成員函數,執行程序都不會有問題呢?答案是否定的!

我們先分別在VC++6.0及VS2010上運行同一段程序,用2、的方法計算2095年與2020年的時間間隔,看看結果如何。

先在vc++6.0上做一按鈕控件,執行如下程序段:


VC++不為人所知的時間編程Bug


這段程序在VC++6.0上進行運算輸出結果是:-1928125696秒,這個結果明顯是一個溢出。時光倒流!

計算結果截圖如下:


VC++不為人所知的時間編程Bug


同樣一段程序,在VS2010上顯示程序如下:

VC++不為人所知的時間編程Bug


程序文本與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卻安然無恙!


分享到:


相關文章: