TDateTime 是 VCL 核心類庫中日期時間運算的基石,以 double 為底層表示(整數部=日期,小數部=時間),提供豐富的運算子重載、格式化輸出、解碼/編碼、檔案時間互轉等功能。本文系統梳理其內存模型、核心 API、格式化語法、高性能運算模式與時區避坑指南。
class TDateTime : public TDateTimeBase {
double val; // 繼承自 TDateTimeBase
};
| val 數值 | 對應日期時間 | 備註 |
|---|---|---|
| 0 | 1899-12-30 00:00:00 | 基準點(OLE Automation 相容) |
| 2.75 | 1900-01-01 18:00:00 | 2 天 + 0.75 天 |
| -1.25 | 1899-12-29 06:00:00 | 負數表基準點之前 |
| 35065 | 1996-01-01 00:00:00 | 整數部即天數 |
關鍵常數:1.0 = 1 天,1.0/24 = 1 小時,1.0/1440 = 1 分鐘,1.0/86400 = 1 秒。
| 簽名 | 用途 |
|---|---|
TDateTime() | 預設構造,val=0 |
TDateTime(double) | 從 OLE Automation 雙精度值構造 |
TDateTime(int) | 從整數天數構造(時間歸零) |
TDateTime(AnsiString, TDateTimeFlag) | 字串解析,Flag 指定 Date/Time/DateTime |
TDateTime(y,m,d) | 日期構造,參數驗證(含閏年) |
TDateTime(h,m,s,ms) | 時間構造,val ∈ [0,1) |
// 日期時間運算直觀如數學
TDateTime now = TDateTime::CurrentDateTime();
TDateTime tomorrow = now + 1; // +1 天
TDateTime nextWeek = now + 7;
TDateTime halfDay = now + 0.5; // +12 小時
TDateTime diff = nextWeek - now; // 差值為 TDateTime(7.0)
int days = int(diff); // 隱式轉 int 取整數天
double hours = diff * 24; // 隱式轉 double 精確小數
// 比較運算子完整支援
if (now < tomorrow) ...
if (now >= TDateTime::CurrentDate()) ...
複合賦值:+=、-=、++、--(前綴/後綴)均可用,-- 以天為單位遞減。
| 方法 | 返回 | 說明 |
|---|---|---|
CurrentDate() | TDateTime | 當地日期 00:00:00 |
CurrentTime() | TDateTime | 當地時間(日期歸零) |
CurrentDateTime() | TDateTime | 當地日期時間 |
FileDateToDateTime(int) | TDateTime | DOS 檔案時間 → TDateTime |
// 解碼到離散分量
void DecodeDate(Word* y, Word* m, Word* d) const;
void DecodeTime(Word* h, Word* m, Word* s, Word* ms) const;
int DayOfWeek() const; // 1=Sun ... 7=Sat
// 編碼由分量構造(靜態輔助函數,非成員)
TDateTime EncodeDate(Word y, Word m, Word d);
TDateTime EncodeTime(Word h, Word m, Word s, Word ms);
AnsiString FormatString(const AnsiString& fmt) 支援下列格式字元(區分大小寫):
| 字元 | 輸出示例 | 說明 |
|---|---|---|
c | 2024-03-15 14:30:05 | ShortDateFormat + LongTimeFormat |
dddd | Friday | 完整星期名 |
ddd | Fri | 縮寫星期名 |
dd | 15 | 兩位日期 |
d | 15 | 不補零日期 |
mmmm | March | 完整月份名 |
mmm | Mar | 縮寫月份名 |
mm | 03 | 兩位月份 |
yyyy | 2024 | 四位年份 |
yy | 24 | 兩位年份 |
hh:nn:ss | 14:30:05 | 24h 時:分:秒 |
h:nn:ss am/pm | 2:30:05 pm | 12h + am/pm |
/ : | - : | 日期/時間分隔符(取自全域變數) |
'text' | text | 字面常數 |
範例:dt.FormatString("yyyy-mm-dd hh:nn:ss") → "2024-03-15 14:30:05"。
operator AnsiString() // 等同 FormatString("c")
operator double() // 底層 val
operator int() // 整數天數
int FileDate() const; // TDateTime → DOS 時間戳
static TDateTime FileDateToDateTime(int); // DOS 時間戳 → TDateTime
// Win32 FILETIME 需自行轉換:
// FILETIME ft; SystemTimeToFileTime(&st, &ft); // 需中間轉 SYSTEMTIME
double d = dt; d += 3.5; dt = d; 避免臨時對象構造。TDateTime 無時區感知,始終視為本地時間。跨時區請:SYSTEMTIME → TzSpecificLocalTimeToSystemTime → SystemTimeToFileTime → FILETIME → ULARGE_INTEGER 運算 → 逆向轉回。ICU / date.h (Howard Hinnant) 等專業時區庫。| 陷阱 | 現象 | 對策 |
|---|---|---|
| 浮點誤差 | (dt1 + 1.0) - dt1 != 1.0 | 比較用 SameDateTime 或容差 fabs(a-b) < 1e-9 |
| 1899 年基準差異 | Unix time_t 基準 1970 | 轉換函數 DateTimeToUnix/UnixToDateTime(自行實現) |
| 夏令時跳變 | 同一本地時間對應兩個/零個 UTC | 存儲/傳輸統一用 UTC (FILETIME),顯示時才轉本地 |
| 格式化全域變數非執行緒安全 | 多執行緒修改 ShortDateFormat 互相干擾 | 改用 FormatString 顯式傳入格式字串 |
TDateTime 以 double 為核心、運算子重載為介面,實現了極致易用的日期時間代數。掌握其底層表示、格式化語法與時區邊界,即可在 VCL 應用中構建健壯的時間業務邏輯。