C語言字符串轉換成日期

 

/*****************************************************************************
 * 版權所有(C) 2006, ZTE Corp. WiMAX
 *----------------------------------------------------------------------------
 * 模 塊 名 : BSPCommon
 * 文件名稱 : str2time.c
 * 文件標識 : {[N/A]}
 * 內容摘要 : 將字符串轉換爲時間
 * 注意事項 : 
 * 作    者 : 田瑞忠
 * 創建日期 : 2008-12-27
 * 當前版本 : Ver1.0
 *----------------------------------------------------------------------------
 * 變更記錄 :
 *
 * $記錄1
 * 變 更 單: $0000000(N/A)
 * 責 任 人: 田瑞忠
 * 修改日戳: 2008-12-27 10:54:17
 * 變更說明: 創建文件
 *
 *----------------------------------------------------------------------------
 */
 
/* 本文件需要引入的外部定義只有 struct tm 這個結構, 它的定義如下:
 *  struct tm
 *  	{
 *  	int tm_sec;	    // seconds after the minute	- [0, 59] 
 *  	int tm_min;	    // minutes after the hour	- [0, 59] 
 *  	int tm_hour;    // hours after 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 Saving Time flag 
 *  	};
 *
 * 在上面這個結構中, 只有年月日時分秒6個必要的元素, 其它的數字可以計算得到.
 * 因爲結構定義年從1900年開始, 而轉換後的統一秒數只能達到136年, 所以目前能夠識別
 * 的年限範圍是在1900~2036年之間. 
 * 本文件提供的字符串轉日期目前只支持常見的中英文格式, 比如年月日, 時分秒, 
 * 通常人能夠直觀讀出的日期讀數, 本文應該都可以識別, 比如:
 *  sep 3, 1995, 20:30      -> 1995-09-03 20:30:00
 *  3sep97                  -> 1997-09-03 00:00:00
 *  oct 4,03                -> 2003-10-04 00:00:00
 *  mon dec 29, 2008        -> 2008-12-29 00:00:00
 *  2011 nov 11, 11:11:11   -> 2011-11-11 11:11:11
 *  10:00am, 2008/9/1       -> 2008-09-01 10:00:00
 *  10/01/1976 3:01:05pm    -> 1976-10-01 15:01:05
 * 但產生歧議的日期, 比如 01/02/03, 本文將按照月份優先, 日期其次, 
 * 最後是年份的格式將被解讀成 2003年1月2日.
 *
 * 本文亦可識別一連串的數字組成的日期格式, 但月日時分秒必須使用兩位十進制數表示.
 *
 * 若您還有其它不明事項, 可以運行strtotimeTest()函數進行檢查. 在移植到PC機運行時
 * 只要改名爲main()函數即可編譯運行(請包含stdio.h頭文件). 作者@2008-12-29
 *
 */
 
#include <time.h>

#define TOKEN_INIT      0
#define TOKEN_VAL       1
#define TOKEN_SEC       2
#define TOKEN_MIN       3
#define TOKEN_HOUR      4
#define TOKEN_HALFDAY   5
#define TOKEN_DAY       6
#define TOKEN_MONTH     7
#define TOKEN_YEAR      8
#define TOKEN_WEEKDAY   9

#define STEP_INIT       0
#define STEP_DATE       1
#define STEP_TIME       2
#define STEP_OVER       3

#define FLAG_SEC        0x0001
#define FLAG_MIN        0x0002
#define FLAG_HOUR       0x0004
#define FLAG_HALFDAY    0x0008
#define FLAG_DAY        0x0010
#define FLAG_MONTH      0x0020
#define FLAG_YEAR       0x0040
#define FLAG_TIME       0x0100
#define FLAG_DATE       0x0200
#define FLAG_ALPHA      0x1000

static char s_cWeekDay[]    = "MonTueWedThuFriSatSun";
static char s_cWeekDayU[]   = "MONTUEWEDTHUFRISATSUN";
static char s_cWeekDayL[]   = "montuewedthufrisatsun";
static char s_cMonth[]      = "JanFebMarAprMayJunJulAugSepOctNovDec";
static char s_cMonthU[]     = "JANFEBMARAPRMAYJUNJULAUTSEPOCTNOVDEC";
static char s_cMonthL[]     = "janfebmaraprmayjunjulaugsepoctnovdec";
static int  s_iMonthFix[]   = {2, 0, 2, 1, 2, 1, 2, 2, 1, 2, 1, 2};

#define IsTimeDelim(a)  ((a) == ':')
#define IsDateDelim(a)  (isspace(a) || (a) == ',' || (a) == '.' || \
                         (a) == '-' || (a) == '/' || (a) == '\\')
#define LeapYear(year)  (((year) % 400 == 0) || \
                            (((year) % 100 != 0) && ((year) % 4 == 0)))

/***************************************************************************** 
 *  函數名稱   : nextTimeToken(*)
 *  功能描述   : 找到下一個時間標記
 *  讀全局變量 : 無
 *  寫全局變量 : 無
 *  輸入參數   : timeStr    - 時間日期字符串
 *  輸出參數   : pVal       - 標記可以轉換的年月日及時間的讀數值
                 pFlag      - 類型標記, 標明解析的是數字, 還是有量綱的值
 *  返 回 值   : 解析過的字符長度, 失敗時返回0
 *  其它說明   : 內部函數, 要求入參指針必須非空
 *---------------------------------------------------------------------------
 * 歷史記錄(變更單, 責任人@修改日期, 操作說明)
 *  $0000000(N/A), 田瑞忠@2008-12-27 16:14:59 創建
 *----------------------------------------------------------------------------
 */
static int nextTimeToken(char *timeStr, int *pVal, int *pFlag)
{
    char    *pChar = timeStr, *pCharEnd;
    int     iFlags = 0, iTemp;

    *pFlag = TOKEN_INIT;

    /* 跳過分隔符 */
    while (IsTimeDelim(*pChar) || IsDateDelim(*pChar))    
    {
        pChar++;
    }

    /* 是數值串, 直接返回數值 */
    if (isdigit(*pChar))
    {
        for (*pVal = 0, pCharEnd = pChar; isdigit(*pCharEnd); pCharEnd++)
        {
            *pVal = *pVal * 10 + (*pCharEnd - '0');
        }

        /* 英文字母序號, 被指定爲day */
        if (*(pCharEnd - 1) == '1' && strncmp(pCharEnd, "st", 2) == 0)
        {
            *pFlag = TOKEN_DAY, pCharEnd += 2;
        }
        else if (*(pCharEnd - 1) == '2' && strncmp(pCharEnd, "nd", 2) == 0)
        {
            *pFlag = TOKEN_DAY, pCharEnd += 2;
        }
        else if (*(pCharEnd - 1) == '3' && strncmp(pCharEnd, "rd", 2) == 0)
        {
            *pFlag = TOKEN_DAY, pCharEnd += 2;
        }
        else if (strncmp(pCharEnd, "th", 2) == 0)
        {
            *pFlag = TOKEN_DAY, pCharEnd += 2;
        }
        
        if (*pFlag == TOKEN_DAY)
        {
            return pCharEnd - timeStr;
        }

#if 1   /* 爲中文服務, 可忽略 */
        /* 跳過空格, 以檢查單位識別符 */
        while (isspace(*pChar))
        {
            pChar++;
        }
        /* 漢字量綱, 被特許爲可識別的日期標識 */
        if (strncmp(pCharEnd, "年", 2) == 0)
        {
            *pFlag = TOKEN_YEAR, pCharEnd += 2;
        }
        else if (strncmp(pCharEnd, "月", 2) == 0)
        {
            *pFlag = TOKEN_MONTH, pCharEnd += 2;
        }
        else if (strncmp(pCharEnd, "日", 2) == 0)
        {
            *pFlag = TOKEN_DAY, pCharEnd += 2;
        }
        else if (strncmp(pCharEnd, "時", 2) == 0)
        {
            *pFlag = TOKEN_HOUR, pCharEnd += 2;
        }
        else if (strncmp(pCharEnd, "分", 2) == 0)
        {
            *pFlag = TOKEN_MIN, pCharEnd += 2;
        }
        else if (strncmp(pCharEnd, "秒", 2) == 0)
        {
            *pFlag = TOKEN_SEC, pCharEnd += 2;
        }
        else
#endif
        {
            *pFlag= TOKEN_VAL;
        }
        return pCharEnd - timeStr;

    } /* (isdigit(*pChar)) */
    
    /* 不是數值且不是字母(其它標點, 不可顯示字符), 一般不被支持, 返回0 */
    if (!isalpha(*pChar))
    {
        if (*pChar >= 0)
        {
            return 0;
        }
        /* 非ASCII字符集, 比如是中文"星期一"之類, 跳過 */
        pCharEnd = pChar;
        while (*pCharEnd != ' ' && *pCharEnd != ',' && *pCharEnd != EOS)
        {
            pCharEnd++;
        }
        return pCharEnd - timeStr;
    } /* (!isdigit(*pChar) && !isalpha(*pChar)) */
        
    /* 是字母, 檢查是不是月份 */
    for (iTemp = 0; iTemp < 12; iTemp++)
    {
        if (strncmp(pChar, s_cMonth + iTemp * 3, 3) == 0)
        {
            *pVal = iTemp + 1;
            break;
        }
        else if (strncmp(pChar, s_cMonthL + iTemp * 3, 3) == 0)
        {
            *pVal = iTemp + 1;
            break;
        }
        else if (strncmp(pChar, s_cMonthU + iTemp * 3, 3) == 0)
        {
            *pVal = iTemp + 1;
            break;
        }
    } /* 月份字母檢查 */

    /* 找到月份字母, 跳過尾部字母 */
    if (iTemp < 12)
    {
        pCharEnd = pChar + 3;
        while (isalpha(*pCharEnd))
        {
            pCharEnd++;
        }
        *pFlag = TOKEN_MONTH;
        return pCharEnd - timeStr;
    }
    
    /* 未找到月份字母, 則檢查星期字母 */
    for (iTemp = 0; iTemp < 7; iTemp++)
    {
        if (strncmp(pChar, s_cWeekDay + iTemp * 3, 3) == 0)
        {
            *pVal = iTemp;
            break;
        }
        else if (strncmp(pChar, s_cWeekDayL + iTemp * 3, 3) == 0)
        {
            *pVal = iTemp;
            break;
        }
        else if (strncmp(pChar, s_cWeekDayU + iTemp * 3, 3) == 0)
        {
            *pVal = iTemp;
            break;
        }
    } /* 星期字母檢查 */

    /* 找到星期字母 */
    if (iTemp < 7)
    {
        pCharEnd = pChar + 3;
        while (isalpha(*pCharEnd))
        {
            pCharEnd++;
        }
        *pFlag = TOKEN_WEEKDAY;
        return pCharEnd - timeStr;
    }
    
    /* 午前午後標誌檢查 */
    if (strncmp(pChar, "am", 2) == 0 || strncmp(pChar, "AM", 2) == 0)
    {
        pCharEnd = pChar + 2;
        if (!isalpha(*pCharEnd))
        {
            *pFlag = TOKEN_HALFDAY;
            *pVal  = 0;
            return pCharEnd - timeStr;
        }
    }
    if (strncmp(pChar, "pm", 2) == 0 || strncmp(pChar, "PM", 2) == 0)
    {
        pCharEnd = pChar + 2;
        if (!isalpha(*pCharEnd))
        {
            *pFlag = TOKEN_HALFDAY;
            *pVal  = 1;
            return pCharEnd - timeStr;
        }
    }
    
    return 0;

} /* nextTimeToken() */

/***************************************************************************** 
 *  函數名稱   : digit2time(*)
 *  功能描述   : 將日期數字轉換爲時間結構, 必須按年月日時分秒排列, 否則看不懂
 *  讀全局變量 : 無
 *  寫全局變量 : 無
 *  輸入參數   : timeStr    - 時間日期字符串
                 iLen       - 時間日期字符串長度
 *  輸出參數   : ptTime     - 日期時間結構
 *  返 回 值   : 年月日時分秒有效的標誌
 *  其它說明   : 歧義和衝突處理, 按排列的優先順序進行
 *---------------------------------------------------------------------------
 * 歷史記錄(變更單, 責任人@修改日期, 操作說明)
 *  $0000000(N/A), 田瑞忠@2008-12-28 10:47:31 創建
 *----------------------------------------------------------------------------
 */
static int digit2time(char *timeStr, int iLen, struct tm *ptTime)
{
    char    *pChar = timeStr;

    memset(ptTime, 0, sizeof(*ptTime));

    if (iLen < 6 || iLen % 2 != 0)
    {
        return 0;
    }

    /* 允許的日期時間格式(按常用頻度排列): 5 + 4 + 3 + 2 + 1 = 15 
        紀年月日時分秒  - YYYYMMDDHHmmss
        年月日時分秒    - YYMMDDHHmmss
        紀年月日時分    - YYYYMMDDHHmm
        月日時分秒      - MMDDHHmmss
        年月日時分      - YYMMDDHHmm
        紀年月日時      - YYYYMMDDHH
        月日時分        - MMDDHHmm
        紀年月日        - YYYYMMDD
        日時分秒        - DDHHmmss
        年月日時        - YYMMDDHH
        年月日          - YYMMDD
        紀年月          - YYYYMM
        時分秒          - HHmmss
        日時分          - DDHHmm
        月日時          - MMDDHH
    */

    /* 超長格式, 只有: YYYYMMDDHHmmss */
    if (iLen >= 14)
    {
        ptTime->tm_year = (pChar[0] - '0') * 1000 + (pChar[1] - '0') * 100 + 
            (pChar[2] - '0') * 10 + (pChar[3] - '0');
        ptTime->tm_mon  = (pChar[4] - '0') * 10 + (pChar[5] - '0');
        ptTime->tm_mday = (pChar[6] - '0') * 10 + (pChar[7] - '0');
        ptTime->tm_hour = (pChar[8] - '0') * 10 + (pChar[9] - '0');
        ptTime->tm_min  = (pChar[10] - '0') * 10 + (pChar[11] - '0');
        ptTime->tm_sec  = (pChar[12] - '0') * 10 + (pChar[13] - '0');
        if (ptTime->tm_year >= 1900 && ptTime->tm_year < 2036 &&
            ptTime->tm_mon < 13 && 
            ptTime->tm_mday <= 29 + s_iMonthFix[ptTime->tm_mon - 1] &&
            ptTime->tm_hour < 24 && ptTime->tm_min < 60 && ptTime->tm_sec < 60)
        {
            return FLAG_SEC | FLAG_MIN | FLAG_HOUR | 
                FLAG_DAY | FLAG_MONTH | FLAG_YEAR;
        }
    }
    /* 次長格式, 有: YYYYMMDDHHmm, YYMMDDHHmmss */
    else if (iLen == 12)
    {
        /* YYYYMMDDHHmm */
        ptTime->tm_year = (pChar[0] - '0') * 1000 + (pChar[1] - '0') * 100 + 
            (pChar[2] - '0') * 10 + (pChar[3] - '0');
        ptTime->tm_mon  = (pChar[4] - '0') * 10 + (pChar[5] - '0');
        ptTime->tm_mday = (pChar[6] - '0') * 10 + (pChar[7] - '0');
        ptTime->tm_hour = (pChar[8] - '0') * 10 + (pChar[9] - '0');
        ptTime->tm_min  = (pChar[10] - '0') * 10 + (pChar[11] - '0');
        if (ptTime->tm_year >= 1900 && ptTime->tm_year < 2036 &&
            ptTime->tm_mon < 13 && 
            ptTime->tm_mday <= 29 + s_iMonthFix[ptTime->tm_mon - 1] &&
            ptTime->tm_hour < 24 && ptTime->tm_min < 60)
        {
            return FLAG_MIN | FLAG_HOUR | FLAG_DAY | FLAG_MONTH | FLAG_YEAR;
        }
        /* YYMMDDHHmmss */
        ptTime->tm_year = (pChar[0] - '0') * 10 + (pChar[1] - '0');
        ptTime->tm_year+= (ptTime->tm_year >= 70) ? 1900 : 2000;
        ptTime->tm_mon  = (pChar[2] - '0') * 10 + (pChar[3] - '0');
        ptTime->tm_mday = (pChar[4] - '0') * 10 + (pChar[5] - '0');
        ptTime->tm_hour = (pChar[6] - '0') * 10 + (pChar[7] - '0');
        ptTime->tm_min  = (pChar[8] - '0') * 10 + (pChar[9] - '0');
        ptTime->tm_sec  = (pChar[10] - '0') * 10 + (pChar[11] - '0');
        if (ptTime->tm_year >= 1900 && ptTime->tm_year < 2036 &&
            ptTime->tm_mon < 13 && 
            ptTime->tm_mday <= 29 + s_iMonthFix[ptTime->tm_mon - 1] &&
            ptTime->tm_hour < 24 && ptTime->tm_min < 60 && ptTime->tm_sec < 60)
        {
            return FLAG_SEC | FLAG_MIN | FLAG_HOUR | 
                FLAG_DAY | FLAG_MONTH | FLAG_YEAR;
        }
    } /* (iLen == 12) */
    /* 10字節, 有: MMDDHHmmss, YYMMDDHHmm, YYYYMMDDHH */
    else if (iLen == 10)
    {
        /* MMDDHHmmss */
        ptTime->tm_mon  = (pChar[0] - '0') * 10 + (pChar[1] - '0');
        ptTime->tm_mday = (pChar[2] - '0') * 10 + (pChar[3] - '0');
        ptTime->tm_hour = (pChar[4] - '0') * 10 + (pChar[5] - '0');
        ptTime->tm_min  = (pChar[6] - '0') * 10 + (pChar[7] - '0');
        ptTime->tm_sec  = (pChar[8] - '0') * 10 + (pChar[9] - '0');
        if (ptTime->tm_mon < 13 && 
            ptTime->tm_mday <= 29 + s_iMonthFix[ptTime->tm_mon - 1] &&
            ptTime->tm_hour < 24 && ptTime->tm_min < 60 && ptTime->tm_sec < 60)
        {
            return FLAG_SEC | FLAG_MIN | FLAG_HOUR | FLAG_DAY | FLAG_MONTH;
        }
        /* YYMMDDHHmm */
        ptTime->tm_year = (pChar[0] - '0') * 10 + (pChar[1] - '0');
        ptTime->tm_year+= (ptTime->tm_year >= 70) ? 1900 : 2000;
        ptTime->tm_mon  = (pChar[2] - '0') * 10 + (pChar[3] - '0');
        ptTime->tm_mday = (pChar[4] - '0') * 10 + (pChar[5] - '0');
        ptTime->tm_hour = (pChar[6] - '0') * 10 + (pChar[7] - '0');
        ptTime->tm_min  = (pChar[8] - '0') * 10 + (pChar[9] - '0');
        if (ptTime->tm_year >= 1900 && ptTime->tm_year < 2036 &&
            ptTime->tm_mon < 13 && 
            ptTime->tm_mday <= 29 + s_iMonthFix[ptTime->tm_mon - 1] &&
            ptTime->tm_hour < 24 && ptTime->tm_min < 60)
        {
            return FLAG_MIN | FLAG_HOUR | FLAG_DAY | FLAG_MONTH | FLAG_YEAR;
        }
        /* YYYYMMDDHH */
        ptTime->tm_year = (pChar[0] - '0') * 1000 + (pChar[1] - '0') * 100 + 
            (pChar[2] - '0') * 10 + (pChar[3] - '0');
        ptTime->tm_mon  = (pChar[4] - '0') * 10 + (pChar[5] - '0');
        ptTime->tm_mday = (pChar[6] - '0') * 10 + (pChar[7] - '0');
        ptTime->tm_hour = (pChar[8] - '0') * 10 + (pChar[9] - '0');
        if (ptTime->tm_hour < 24 && 
            ptTime->tm_year >= 1900 && ptTime->tm_year < 2036 &&
            ptTime->tm_mon < 13 && 
            ptTime->tm_mday <= 29 + s_iMonthFix[ptTime->tm_mon - 1])
        {
            return FLAG_HOUR | FLAG_DAY | FLAG_MONTH | FLAG_YEAR;
        }
    } /* (iLen == 10) */
    /* 8字節, 有: MMDDHHmm, YYYYMMDD, DDHHmmss, YYMMDDHH */
    else if (iLen == 8)
    {
        /* MMDDHHmm */
        ptTime->tm_mon  = (pChar[0] - '0') * 10 + (pChar[1] - '0');
        ptTime->tm_mday = (pChar[2] - '0') * 10 + (pChar[3] - '0');
        ptTime->tm_hour = (pChar[4] - '0') * 10 + (pChar[5] - '0');
        ptTime->tm_min  = (pChar[6] - '0') * 10 + (pChar[7] - '0');
        if (ptTime->tm_mon < 13 && 
            ptTime->tm_mday <= 29 + s_iMonthFix[ptTime->tm_mon - 1] &&
            ptTime->tm_hour < 24 && ptTime->tm_min < 60)
        {
            return FLAG_MIN | FLAG_HOUR | FLAG_DAY | FLAG_MONTH;
        }
        /* YYYYMMDD */
        ptTime->tm_year = (pChar[0] - '0') * 1000 + (pChar[1] - '0') * 100 + 
            (pChar[2] - '0') * 10 + (pChar[3] - '0');
        ptTime->tm_mon  = (pChar[4] - '0') * 10 + (pChar[5] - '0');
        ptTime->tm_mday = (pChar[6] - '0') * 10 + (pChar[7] - '0');
        if (ptTime->tm_year >= 1900 && ptTime->tm_year < 2036 &&
            ptTime->tm_mon < 13 && 
            ptTime->tm_mday <= 29 + s_iMonthFix[ptTime->tm_mon - 1])
        {
            return FLAG_DAY | FLAG_MONTH | FLAG_YEAR;
        }
        /* DDHHmmss */
        ptTime->tm_mday = (pChar[0] - '0') * 10 + (pChar[1] - '0');
        ptTime->tm_hour = (pChar[2] - '0') * 10 + (pChar[3] - '0');
        ptTime->tm_min  = (pChar[4] - '0') * 10 + (pChar[5] - '0');
        ptTime->tm_sec  = (pChar[6] - '0') * 10 + (pChar[7] - '0');
        if (ptTime->tm_mday <= 31 && ptTime->tm_hour < 24 && 
            ptTime->tm_min < 60 && ptTime->tm_sec < 60)
        {
            return FLAG_SEC | FLAG_MIN | FLAG_HOUR | FLAG_DAY;
        }
        /* YYMMDDHH */
        ptTime->tm_year = (pChar[0] - '0') * 10 + (pChar[1] - '0');
        ptTime->tm_year+= (ptTime->tm_year >= 70) ? 1900 : 2000;
        ptTime->tm_mon  = (pChar[2] - '0') * 10 + (pChar[3] - '0');
        ptTime->tm_mday = (pChar[4] - '0') * 10 + (pChar[5] - '0');
        ptTime->tm_hour = (pChar[6] - '0') * 10 + (pChar[7] - '0');
        if (ptTime->tm_hour < 24 && 
            ptTime->tm_year >= 1900 && ptTime->tm_year < 2036 &&
            ptTime->tm_mon < 13 && 
            ptTime->tm_mday <= 29 + s_iMonthFix[ptTime->tm_mon - 1])
        {
            return FLAG_HOUR | FLAG_DAY | FLAG_MONTH | FLAG_YEAR;
        }
    } /* (iLen == 8) */
    else
    {
        /* YYMMDD */
        ptTime->tm_year = (pChar[0] - '0') * 10 + (pChar[1] - '0');
        ptTime->tm_year+= (ptTime->tm_year >= 70) ? 1900 : 2000;
        ptTime->tm_mon  = (pChar[2] - '0') * 10 + (pChar[3] - '0');
        ptTime->tm_mday = (pChar[4] - '0') * 10 + (pChar[5] - '0');
        if (ptTime->tm_year >= 1900 && ptTime->tm_year < 2036 &&
            ptTime->tm_mon < 13 && 
            ptTime->tm_mday <= 29 + s_iMonthFix[ptTime->tm_mon - 1])
        {
            return FLAG_DAY | FLAG_MONTH | FLAG_YEAR;
        }
        /* YYYYMM */
        ptTime->tm_year = (pChar[0] - '0') * 1000 + (pChar[1] - '0') * 100 + 
            (pChar[2] - '0') * 10 + (pChar[3] - '0');
        ptTime->tm_mon  = (pChar[4] - '0') * 10 + (pChar[5] - '0');
        if (ptTime->tm_year >= 1900 && ptTime->tm_year < 2036 && 
            ptTime->tm_mon < 13)
        {
            return FLAG_MONTH | FLAG_YEAR;
        }
        /* HHmmss */
        ptTime->tm_hour = (pChar[0] - '0') * 10 + (pChar[1] - '0');
        ptTime->tm_min  = (pChar[2] - '0') * 10 + (pChar[3] - '0');
        ptTime->tm_sec  = (pChar[4] - '0') * 10 + (pChar[5] - '0');
        if (ptTime->tm_hour < 24 && ptTime->tm_min < 60 && ptTime->tm_sec < 60)
        {
            return FLAG_SEC | FLAG_MIN | FLAG_HOUR;
        }
        /* DDHHmm */
        ptTime->tm_mday = (pChar[0] - '0') * 10 + (pChar[1] - '0');
        ptTime->tm_hour = (pChar[2] - '0') * 10 + (pChar[3] - '0');
        ptTime->tm_min  = (pChar[4] - '0') * 10 + (pChar[5] - '0');
        if (ptTime->tm_mday <= 31 && ptTime->tm_hour < 24 && ptTime->tm_min < 60)
        {
            return FLAG_MIN | FLAG_HOUR | FLAG_DAY;
        }
        /* MMDDHH */
        ptTime->tm_mon  = (pChar[0] - '0') * 10 + (pChar[1] - '0');
        ptTime->tm_mday = (pChar[2] - '0') * 10 + (pChar[3] - '0');
        ptTime->tm_hour = (pChar[4] - '0') * 10 + (pChar[5] - '0');
        if (ptTime->tm_mon < 13 && 
            ptTime->tm_mday <= 29 + s_iMonthFix[ptTime->tm_mon - 1] && 
            ptTime->tm_hour < 24)
        {
            return FLAG_HOUR | FLAG_DAY | FLAG_MONTH;
        }
    } /* (iLen == 6) */

    return 0;
} /* digit2time() */

/***************************************************************************** 
 *  函數名稱   : strtotime(*)
 *  功能描述   : 將日期及(或)時間字符串轉換爲時間結構, 例: Dec 19 2008, 13:35:40
 *  讀全局變量 : 無
 *  寫全局變量 : 無
 *  輸入參數   : timeStr    - 時間日期字符串
 *  輸出參數   : ptTime     - 日期時間結構
 *  返 回 值   : 年月日時分秒有效的標誌
 *  其它說明   : 時分秒之間必須使用冒號":"分隔, 並且必然是此順序
 *---------------------------------------------------------------------------
 * 歷史記錄(變更單, 責任人@修改日期, 操作說明)
 *  $0000000(N/A), 田瑞忠@2008-12-27 16:14:59 創建
 *----------------------------------------------------------------------------
 */
static int strtotime(char *timeStr, struct tm *ptTime)
{
    int     iFlags, iVal, iLen, iStep, iDateTimeFlag;
    int     iYear, iMonth, iDay, iHour, iMinute, iSecond;
    char    *pChar = timeStr, *pCharEnd = timeStr, tempStr[8];

    iDateTimeFlag = iYear = iMonth = iDay = iHour = iMinute = iSecond = 0;
    memset(ptTime, 0, sizeof(*ptTime));

    /* 跳過前導空格 */
    while (*pChar == ' ' || *pChar == '\t')    
    {
        pChar++;
    }

    /* 不存在可解析的中間字符串 */
    if ((iLen = nextTimeToken(pChar, &iVal, &iFlags)) <= 0)
    {
        return iDateTimeFlag;
    }

    /* 數值串且長度大於等於6, 則爲全數字型日期時間, 如20081229080306 
     * 需要重新解析 */
    if (iFlags == TOKEN_VAL && iLen >= 6 && iLen % 2 == 0)
    {
        iDateTimeFlag = digit2time(pChar, iLen, ptTime);
        if (iDateTimeFlag == 0)
        {
            memset(ptTime, 0, sizeof(*ptTime));
        }
        /* 由於進行了值嘗試, 當不適用時, 要清除嘗試值 */
        ptTime->tm_year =
                (iDateTimeFlag & FLAG_YEAR) ? ptTime->tm_year - 1900 : 0;

        ptTime->tm_mon  =
                (iDateTimeFlag & FLAG_MONTH) ? ptTime->tm_mon - 1 : 0;

        if ((iDateTimeFlag & FLAG_DAY) == 0)
        {
            ptTime->tm_mday = 1;
        }
        if ((iDateTimeFlag & FLAG_HOUR) == 0)
        {
            ptTime->tm_hour = 0;
        }
        if ((iDateTimeFlag & FLAG_MIN) == 0)
        {
            ptTime->tm_min = 0;
        }
        if ((iDateTimeFlag & FLAG_SEC) == 0)
        {
            ptTime->tm_sec = 0;
        }
        if (!iDateTimeFlag)
        {
            goto strToTimeErrOut;
        }
        return iDateTimeFlag;
    }

    /* 否則各字段之間必然有分隔符(或數字英文分隔, 比如 22sep92) */
    for (iStep = STEP_INIT; *pChar != EOS && iLen > 0;
        pChar += iLen, iLen = nextTimeToken(pChar, &iVal, &iFlags))
    {
        /* 強格式年份, 直接填充到年份信息中 */
        if (iFlags == TOKEN_YEAR)
        {
            if ((iDateTimeFlag & FLAG_YEAR) == 0)
            {
                iYear = iVal, iDateTimeFlag |= FLAG_YEAR;
            }
            else
            {
                goto strToTimeErrOut;
            }
            iStep = STEP_DATE, iDateTimeFlag |= FLAG_DATE;
            continue;
        }
        /* 強格式月份, 直接填充到月份信息中(否則需要試探比較) */
        else if (iFlags == TOKEN_MONTH)
        {
            if ((iDateTimeFlag & FLAG_MONTH) == 0)
            {
                iMonth = iVal, iDateTimeFlag |= FLAG_ALPHA | FLAG_MONTH;
            }
            /* 有月份信息但非字母指定, 說明是試探, 則將其移走(值必居於1~12) */
            else if ((iDateTimeFlag & FLAG_ALPHA) == 0)
            {
                if ((iDateTimeFlag & FLAG_DAY) == 0)
                {
                    iDay = iMonth, iDateTimeFlag |= FLAG_DAY;
                }
                else if ((iDateTimeFlag & FLAG_YEAR) == 0) /* 2001 ~ 2012年 */
                {
                    iYear = iMonth, iDateTimeFlag |= FLAG_YEAR;
                }
                else
                {
                    goto strToTimeErrOut;
                }
                iMonth = iVal, iDateTimeFlag |= FLAG_ALPHA;
            }
            else
            {
                goto strToTimeErrOut;
            }
            iStep = STEP_DATE, iDateTimeFlag |= FLAG_DATE;
            continue;
        } /* (iFlags == TOKEN_MONTH) */
        /* 強格式日期, 直接填充到日期信息中 */
        else if (iFlags == TOKEN_DAY)
        {
            if ((iDateTimeFlag & FLAG_DAY) == 0)
            {
                iDay = iVal, iDateTimeFlag |= FLAG_DAY;
            }
            else
            {
                goto strToTimeErrOut;
            }
            iStep = STEP_DATE, iDateTimeFlag |= FLAG_DATE;
            continue;
        }
        /* 半日信息(上午, 下午, 必然在時間後, 不然不予認可) */
        else if (iFlags == TOKEN_HALFDAY)
        {
            if ((iDateTimeFlag & FLAG_HALFDAY) == 0 &&
                iHour + iVal * 12 < 24)
            {
                iHour += iVal * 12, iDateTimeFlag |= FLAG_HALFDAY;
            }
            else
            {
                goto strToTimeErrOut;
            }
            iStep = STEP_TIME, iDateTimeFlag |= FLAG_TIME;
            continue;
        }
        /* 強格式小時, 直接填充到小時信息中 */
        else if (iFlags == TOKEN_HOUR)
        {
            if ((iDateTimeFlag & FLAG_HOUR) == 0 && iVal < 24)
            {
                iHour = iVal, iDateTimeFlag |= FLAG_HOUR;
            }
            else
            {
                goto strToTimeErrOut;
            }
            iStep = STEP_TIME, iDateTimeFlag |= FLAG_TIME;
            continue;
        }
        /* 強格式分鐘, 直接填充到分鐘信息中 */
        else if (iFlags == TOKEN_MIN)
        {
            if ((iDateTimeFlag & FLAG_MIN) == 0 && iVal < 60)
            {
                iMinute = iVal, iDateTimeFlag |= FLAG_MIN;
            }
            else
            {
                goto strToTimeErrOut;
            }
            iStep = STEP_TIME, iDateTimeFlag |= FLAG_TIME;
            continue;
        }
        /* 強格式秒, 直接填充到秒信息中 */
        else if (iFlags == TOKEN_SEC)
        {
            if ((iDateTimeFlag & FLAG_SEC) == 0 && iVal < 60)
            {
                iSecond = iVal, iDateTimeFlag |= FLAG_SEC;
            }
            else
            {
                goto strToTimeErrOut;
            }
            iStep = STEP_TIME, iDateTimeFlag |= FLAG_TIME;
            continue;
        }

        /* 不是數值, 跳過 */
        if (iFlags != TOKEN_VAL)
        {
            continue;
        }

        /* 標記尾碰到時間分隔符, 則直接進入時間信息處理區 */
        if (IsTimeDelim(pChar[iLen]))
        {
            if ((iDateTimeFlag & FLAG_TIME) == 0)
            {
                iStep = STEP_TIME, iDateTimeFlag |= FLAG_TIME;
            }
            /* 如果時間信息已獲取, 且在處理日期狀態, 則跳過 */
            else if (iStep == STEP_DATE)
            {
                iStep = STEP_OVER;
            }
        }
        /* 時間信息處理區, 但沒有碰到時間分隔符, 則要切換處理區 */
        else if (iStep == STEP_TIME)
        {
            if (IsTimeDelim(pChar[0]))
            {
                /* 最後一個字段, 狀態保持 */;
            }
            else if ((iDateTimeFlag & FLAG_DATE) == 0)
            {
                iStep = STEP_DATE, iDateTimeFlag |= FLAG_DATE;
            }
            /* 如果日期信息已獲取, 且在處理時間狀態, 則跳過 */
            else
            {
                iStep = STEP_OVER;
            }
        }
        /* 默認情況下都是處理日期 */
        else
        {
            iStep = STEP_DATE;
        }

        /* 處理時間: 時分秒, 逐次檢查(秒可能被省略) */
        if (iStep == STEP_TIME)
        {
            if ((iDateTimeFlag & FLAG_HOUR) == 0)
            {
                if (iVal >= 0 && iVal <= 23)
                {
                    iHour = iVal, iDateTimeFlag |= FLAG_HOUR;
                }
                else
                {
                    goto strToTimeErrOut;
                }
            }
            else if ((iDateTimeFlag & FLAG_MIN) == 0)
            {
                if (iVal >= 0 && iVal <= 59)
                {
                    iMinute = iVal, iDateTimeFlag |= FLAG_MIN;
                }
                else
                {
                    goto strToTimeErrOut;
                }
            }
            else if ((iDateTimeFlag & FLAG_SEC) == 0)
            {
                if (iVal >= 0 && iVal <= 59)
                {
                    iSecond = iVal, iDateTimeFlag |= FLAG_SEC;
                }
                else
                {
                    goto strToTimeErrOut;
                }
            }
            else /* 多餘的時間數據, 則認爲字符串不合法 */
            {
                goto strToTimeErrOut;
            }

            continue;

        } /* if (iStep == STEP_TIME) */

        /* 日期處理, 日期格式只有三種: 月日年, 日月年, 年月日, 03oct01? 
         * 歧議日期一律按"月日年"順序處理, 上例是01年10月3日而不是03年10月1日 */
        
        /* 四位數字年份, 毫無疑問直接填充 */
        if (iVal >= 1900 && iVal <= 2036)
        {
            if ((iDateTimeFlag & FLAG_YEAR) == 0)
            {
                iYear = iVal, iDateTimeFlag |= FLAG_YEAR;
            }
            else
            {
                goto strToTimeErrOut;
            }
        }
        /* 兩位數字無爭議年份, 填充 */
        else if (iVal == 0 || (iVal >= 70 && iVal <= 99))
        {
            if ((iDateTimeFlag & FLAG_YEAR) == 0)
            {
                iYear = iVal, iDateTimeFlag |= FLAG_YEAR;
            }
            else
            {
                goto strToTimeErrOut;
            }
        }
        else if (iVal <= 12)    /* 小於12的數字, 年月日都可以 */
        {
            /* 寫入月份讀數時要檢測日期讀數衝突 */
            if ((iDateTimeFlag & FLAG_MONTH) == 0)
            {
                if ((iDateTimeFlag & FLAG_DAY) == 0 ||
                    iDay <= 29 + s_iMonthFix[iVal - 1])
                {
                    iMonth = iVal, iDateTimeFlag |= FLAG_MONTH;
                }
                else if ((iDateTimeFlag & FLAG_YEAR) == 0)
                {
                    iYear = iVal, iDateTimeFlag |= FLAG_YEAR;
                }
                else
                {
                    goto strToTimeErrOut;
                }
            }
            else if ((iDateTimeFlag & FLAG_DAY) == 0)
            {
                iDay = iVal, iDateTimeFlag |= FLAG_DAY;
            }
            else if ((iDateTimeFlag & FLAG_YEAR) == 0)
            {
                iYear = iVal, iDateTimeFlag |= FLAG_YEAR;
            }
            else
            {
                goto strToTimeErrOut;
            }
        }
        else if (iVal <= 31 /* && iVal > 12 */)
        {
            /* 寫入日期讀數時要檢測月份讀數衝突 */
            if ((iDateTimeFlag & FLAG_DAY) == 0)
            {
                if ((iDateTimeFlag & FLAG_MONTH) == 0 ||
                    iVal <= 29 + s_iMonthFix[iMonth - 1])
                {
                    iDay = iVal, iDateTimeFlag |= FLAG_DAY;
                }
                else if ((iDateTimeFlag & FLAG_YEAR) == 0)
                {
                    iYear = iVal, iDateTimeFlag |= FLAG_YEAR;
                }
                else
                {
                    goto strToTimeErrOut;
                }
            }
            else if ((iDateTimeFlag & FLAG_YEAR) == 0)
            {
                iYear = iVal, iDateTimeFlag |= FLAG_YEAR;
            }
            else
            {
                goto strToTimeErrOut;
            }
        }
        else if (iVal > 31 && iVal <= 36)
        {
            if ((iDateTimeFlag & FLAG_YEAR) == 0)
            {
                iYear = iVal, iDateTimeFlag |= FLAG_YEAR;
            }
            else
            {
                goto strToTimeErrOut;
            }
        }
        else /* 不合法的讀數 */
        {
            goto strToTimeErrOut;
        }
            
    } /* while (*pChar != EOS && iLen > 0) */

    /* 對於存在歧議的日期及時間, 目前的默認順序是: 月份, 日期, 年份
     * 使用這個規則的原因在於, 月份的限制最嚴格(1~12), 其次日期(1~31), 年份任意,
     * 按這個順序填入日期讀數, 可以避免衝突數據的搬移操作, 
     * 要改變這個默認規則, 只需在下面檢查是否存在歧議, 然後修改順序即可 */

    /* 年份讀數轉換爲內部表示 */
    if (iDateTimeFlag & FLAG_YEAR)
    {
        if (iYear > 2036)
        {
            goto strToTimeErrOut;
        }
        else if (iYear >= 1900)
        {
            ptTime->tm_year = iYear - 1900;
        }
        else if (iYear >= 0 && iYear < 36)
        {
            ptTime->tm_year = iYear + 100;
        }
        else if (iYear >= 70 && iYear <= 99)
        {
            ptTime->tm_year = iYear;
        }
        else
        {
            goto strToTimeErrOut;
    }
    } /* (iDateTimeFlag & FLAG_YEAR) */

    /* 月份讀數也要轉換爲內部表示(0~11) */
    if (iDateTimeFlag & FLAG_MONTH)
    {
        if (iMonth < 1 || iMonth > 12)
        {
            goto strToTimeErrOut;
        }
        ptTime->tm_mon  = iMonth - 1;
    }

    /* 日期, 要查看是否滿足條件 */
    if (iDateTimeFlag & FLAG_DAY)
    {
        if ((iDay > 29 + s_iMonthFix[ptTime->tm_mon]) ||
            (ptTime->tm_mon == 1 && !LeapYear(iYear) && iDay > 28))
        {
            goto strToTimeErrOut;
        }
    ptTime->tm_mday = iDay;
    }
    else
    {
        ptTime->tm_mday = 1;
    }
    
    /* 時間值在賦值時都是合法的 */
    ptTime->tm_hour = iHour;
    ptTime->tm_min  = iMinute;
    ptTime->tm_sec  = iSecond;
    
    return iDateTimeFlag;

strToTimeErrOut:
    memset(ptTime, 0, sizeof(*ptTime));
    ptTime->tm_mday = 1;
    printf("Unrecognized date and/or time string:\n%s\n", timeStr);
    return 0;
} /* strtotime() */

/***************************************************************************** 
 *  函數名稱   : strtotimeTest(*)
 *  功能描述   : 字符串轉換爲日期時間函數測試, 可以反覆驗證轉換函數的正確性 
 *  讀全局變量 : 無
 *  寫全局變量 : 無
 *  輸入參數   : 等待用戶輸入
 *  輸出參數   : 無
 *  返 回 值   : OK
 *  其它說明   : 修改爲main()函數後可在PC上調試
 *---------------------------------------------------------------------------
 * 歷史記錄(變更單, 責任人@修改日期, 操作說明)
 *  $0000000(N/A), 田瑞忠@2008-12-28 20:42:26
 *----------------------------------------------------------------------------
 */
int strtotimeTest(void)
{
    char    charbuf[80];
    struct tm timeStru;
    int     iret;

    while (1)
    {
        printf("Input string to be converted, 'q' for quit:\n");
        gets(charbuf);
        if (charbuf[0] == 'q')
        {
            break;
        }
        iret = strtotime(charbuf, &timeStru);
        printf("conver result %d: %04d-%02d-%02d, %02d:%02d:%02d\n", iret,
            timeStru.tm_year + 1900, timeStru.tm_mon + 1, timeStru.tm_mday,
            timeStru.tm_hour, timeStru.tm_min, timeStru.tm_sec);
    }

    return iret;

} /* strtotimeTest() */


 

 

mktime(將時間結構數據轉換成經過的秒數) 
相關函數  time,asctime,gmtime,localtime

表頭文件  #include<time.h>

定義函數  time_t mktime(strcut tm * timeptr);

函數說明  mktime()用來將參數timeptr所指的tm結構數據轉換成從公元1970年1月1日0時0分0 秒算起至今的UTC時間所經過的秒數。

返回值  返回經過的秒數。

將兩個數都mktime,然後求差

 

發佈了14 篇原創文章 · 獲贊 6 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章