一位網友提的問題。

原問題是欲以 script language : AutoIt 完成。給定一準確時間點 t ,計算 1970/1/1  00:00:00 至該時間點 t 所經歷秒數。

這在 C 語言裡面很容易辦到 < 我甚至懷疑要處理的來原資料是用 time_t 存的 >,步驟大致如下。

 

(1) 將計算時間填入 struct tm

(2) struct tm 轉成 time_t : mktime

(3) time_t 內容即為所求。


OK,翻開 autoit,很明顯沒辦法調用 mktime 函式,然後 autoit 它的時間系統真的也和一般程式語言不同,它沒有用一個整數去做標記的概念,所以不能像 C 這樣如法炮製。不過 autoit 可以調用 win32 API,所以步驟變成下面。


(1) 將開始時間(1970/1/1 00:00:00)、結束時間填入 SYSTEMTIME

(2) 將兩個時間點之 SYSTEMTIME 轉成 FILETIME :  SystemTimeToFileTime

(3) 將那個時間點之 FILETIME assigned to ULARGE_INTEGER:將 FILETIME 裡之 dwLowDateTime 設給 ULARGE_INTEGER 裡之 LowPart ;dwHighDataTime 設給 HighPare。

(4) 使用 ULARGE_INTEGER 裡頭成員之 QuadPart 做相減,算出來之結果是以 10 ns 為差值單位。

 

尷尬的問題來了autoit 可以做 struct 但不能做 union,這問題還是小問題,比較嚴重的問題是,autoit 不支援 64bits 整數資料型態

 

本有兩個方法可以解決,一個是一樣硬性調用 Win32 API ,只是後半段得到 FILETIME 後,取得之時間再用大數演算法做減法,這條路估計會比較複雜(特別是套用在很高階的 script language 身上),所以沒走這裡。

另一條路不用講了,自己手動刻,幸運的是之前該做好的函式都已經做好了,參考 [C&++] 時間函式與議題,只是再把函式轉成 script langauge 而已。假設欲計算的年份是 year,運用之前已刻好的函式,步驟大致如下。

 

(1) 先計算 [1970, year-1] 裡有幾個平年、幾個潤年 

之前計算 [year_beg, year_end] 裡面有幾個潤年,公式為 
 
int c1 = year_beg / 4 - (year_beg / 100) + (year_beg / 400); // [1, year_beg] 有幾個潤年
int c2 = year_end / 4 - (year_end / 100) + (year_end / 400);// [1, year_end] 有幾個潤年
int leap_cnt = c2 - c1 + is_leap_year(year_beg) ; // 計算 [year_beg, year_end] 有幾個潤年
 
有幾個平年就是  int non_leap_cnt = (year_end - year_beg + 1) - leap_cnt; 套用上述公式可得到結果,常數 1970 可事先套入計算。
 
然後事先計算 365 天有 31536000 秒,366 天有 31622400 秒,得到了 [1970, year-1] 過了 
leap_cnt * 31622400 + non_leap_cnt * 31536000 秒
 
(2) 再計算日期是該年的第幾天
 
之前的文章也提過
const int column_days[] = { \
  0, 0, 31, 59, 90, 120, 151, \
  181, 212, 243, 273, 304, 334};
int find_nday(int year, int month, int day)
{
    return column_days[month] + day + \
        (month > 2 && is_leap_year(year));
}

然後一天有 86400 秒,注意的是,上面副函式的傳回值,1/1 是第 1 天,但這程式需求是 1/1 要算第 0 天。所以該天之前經過幾秒

86400 * ( find_nday(year, month, day) -1 ) 秒


(3) 其他與累計加總


接下來的 hours、mins、secs 很單純的用倍率求出,把上面兩部份求出來結果相加,就是答案。問題解後筆者沒再留 code,有興趣的可自行試試。
 

edisonx 發表在 痞客邦 PIXNET 留言(0) 人氣()