2010-08-03 45 views
0

我寫了一個功能,日期時間從一種格式轉換爲另一種 -的strftime/strptime問題


int convertDateTime(char* source_fmt,char* dest_fmt,char* source,char* dest) 
{ 
     struct tm tmpptr; 
     if (strptime(source,source_fmt,&tmpptr) == NULL) 
     { 
       strcpy(dest,""); 
       return -1; 
     } 
     strftime(dest,100,dest_fmt,&tmpptr); 
     return 0; 
} 

它的工作原理適用於大多數的格式,但是當我使用格式=「%Y%J」,所有我得到的是10001;朱連安日不工作。

我在solaris 10上使用gcc。任何想法我需要改變?

回答

-1

更新
最初的答案(下文)假設在輸出(strftime)上使用了「%y%j」格式,而不是輸入(strptime)。 mktime函數將從有效信息計算yday,但它不會以其他方式運行。

如果你想解碼這樣的事情,你需要手動完成。以下是一些示例代碼。它已經進行了最低限度的測試,幾乎肯定存在漏洞。您可能需要更改ir,具體取決於所需的輸入格式。

#include <string> 
#include <ctime> 
#include <cassert> 
#include <iostream> 

int GetCurrentYear() 
{ 
    time_t tNow(::time(NULL)); 
    struct tm tmBuff = *::localtime(&tNow); 
    return tmBuff.tm_year; 
} 
bool IsLeapYear(int nYear) 
{ 
    if (0 == (nYear%1000)) return true; 
    if (0 == (nYear%100)) return false; 
    if (0 == (nYear%4)) return true; 
    return false; 
} 
// nMonth = 0 (Jan) to 11 (Dec) 
int DaysPerMonth(int nMonth, bool bLeapYear) 
{ 
    //     J F M A M J J A S O N D 
    int nDays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 
    assert(nMonth>=0 && nMonth<12); 
    int nRet = nDays[nMonth]; 
    if (bLeapYear && nMonth==1) 
     nRet++; 
    return nRet; 
} 

// sDate is in the format YYDDD where YY is the last 2 digits of the year 
// and YYY is the day of the year (1/1 = 1, 31/12 = 365 for non-leap year) 
bool DecodeDate(const std::string &sDate, struct tm &tmBuff) 
{ 
    if (sDate.length() != 5 || 
     !isdigit(sDate[0]) || 
     !isdigit(sDate[1]) || 
     !isdigit(sDate[2]) || 
     !isdigit(sDate[3]) || 
     !isdigit(sDate[4])) 
    { 
     return false; 
    } 
    ::memset(&tmBuff, 0, sizeof(struct tm)); 
    tmBuff.tm_year = GetCurrentYear(); 
    // drop last 2 digits 
    tmBuff.tm_year -= tmBuff.tm_year%100; 
    // replace last 2 digits 
    tmBuff.tm_year += ::atoi(sDate.substr(0, 2).c_str());  

    tmBuff.tm_yday = ::atoi(sDate.substr(2).c_str()); 
    int nDays(tmBuff.tm_yday); 
    bool bLeapYear(IsLeapYear(1900 + tmBuff.tm_year)); 
    int nTmp = DaysPerMonth(0, bLeapYear); 
    while (nTmp < nDays) 
    { 
     nDays -= nTmp; 
     tmBuff.tm_mon++; 
     nTmp = DaysPerMonth(tmBuff.tm_mon, bLeapYear); 
    } 
    tmBuff.tm_mday = nDays; 
    ::mktime(&tmBuff); 
    return true; 
} 

int main(int argc, char *argv[]) 
{ 
    for (int i=1; i<argc; i++) 
    { 
     struct tm tmBuff; 
     DecodeDate(argv[i], tmBuff); 
     const size_t nSize(128); 
     char szBuff[nSize]; 
     strftime(szBuff, nSize, "%A, %d %B %Y", &tmBuff); 
     std::cout << argv[i] << '\t' << szBuff << std::endl; 
    } 
    return 0; 
} 

=========================================== =====

C:\Dvl\Tmp>Test.exe 07123 08123 08124 
07123 Thursday, 03 May 2007 
08123 Friday, 02 May 2008 
08124 Saturday, 03 May 2008 

末更新

在調用strptime()調用mktime()將填充結構的任何缺少的成員。另外,你應該在開始之前將結構清零。

#include <string> 
#include <ctime> 

int convertDateTime(const std::string &sSourceFmt, 
        const std::string &sDestFmt, 
        const std::string &sSource, 
        std::string &sDest) 
{ 
    struct tm tmbuff = { 0 }; 

    if (::strptime(sSource.c_str(), sSourceFmt.c_str(), &tmbuff) != NULL) 
    { 
     ::mktime(&tmbuff); 
     const size_t nSize(256); 
     char szBuff[nSize+1] = ""; 

     if (::strftime(szBuff, nSize, sDestFmt.c_str(), &tmbuff)) 
     { 
      sDest = szBuff; 
      return 0; 
     } 
    } 
    sDest.clear(); 
    return -1; 
} 
+0

謝謝!這完美的作品! – vineet 2010-08-03 07:12:47

+0

我真的很驚訝的作品。它不在Cygwin gcc下,只有tm_year和tm_yday被設置,事實上,你引用的doco聲明「timeptr結構的tm_wday和tm_yday組件的原始值被忽略」。但是,如果OP說沒關係,我猜沒關係:-) – paxdiablo 2010-08-03 07:28:36

+0

實際上,_still_在CygWin下不起作用,glibc 2.9的源代碼在其計算中似乎沒有使用tm_yday,所以我仍然不服氣。你可以用'%y%j'的輸入格式,輸入值'10032'和輸出格式'%Y-%m-%d'來試試它,看看它做了什麼? – paxdiablo 2010-08-03 09:18:42

0

檢查strptime的預期行爲,在某些實現中,它是貪婪的並且會消耗盡可能多的數字。

這對我來說是一個問題,在MacOS上,實現在UNIX標準合規工作中更改爲10.5。在此之前,以下呼籲工作正常。

strptime("20071124", "%Y%m%d") 

從10.5開始,您必須執行以下操作才能使其工作。

#define _NONSTD_SOURCE 

雖然您的編譯器庫和操作系統可能會有所不同。

0

我可以告訴你問題是什麼。當您使用strptime()%j格式時,它只填充struct tm上的tm_yday字段。順便說一下,這不僅限於Solaris,CygWin gcc也在做同樣的事情。

因爲你strftime()是最有可能使用的其他領域(tm_montm_mday),而這些仍然設置爲零,這就是爲什麼你得到一年的錯誤的一天。

下面的代碼說明了這一點:

#include <time.h> 
#include <stdio.h> 
#include <string.h> 

#define dump() \ 
    printf ("DEBUG tm_sec = %d, tm_min = %d, tm_hour = %d, tm_mday = %d, " \ 
     "tm_mon = %d, tm_year = %d, tm_wday = %d, tm_yday = %d, " \ 
     "tm_isdst = %d\n", \ 
     tmpptr.tm_sec, tmpptr.tm_min, tmpptr.tm_hour, tmpptr.tm_mday, \ 
     tmpptr.tm_mon, tmpptr.tm_year, tmpptr.tm_wday, tmpptr.tm_yday, \ 
     tmpptr.tm_isdst) 

int convertDateTime(char* source_fmt,char* dest_fmt,char* source,char* dest) { 
    struct tm tmpptr; 
    memset (&tmpptr,0,sizeof(tmpptr)); 
    dump(); 
    if (strptime(source,source_fmt,&tmpptr) == NULL) { 
      strcpy(dest,""); 
      return -1; 
    } 
    dump(); 
    strftime(dest,100,dest_fmt,&tmpptr); 
    return 0; 
} 

int main (int argc, char *argv[]) { 
    char dest[1000]; 
    printf ("1: [%s]\n", argv[1]); 
    printf ("2: [%s]\n", argv[2]); 
    printf ("3: [%s]\n", argv[3]); 
    printf ("retval = %d\n", convertDateTime (argv[1],argv[2],argv[3],dest)); 
    printf ("=: [%s]\n", dest); 
    return 0; 
} 

當你這樣運行:

pax> date ; ./tetsprog %y%j %Y-%m-%d 10162 

你:

Tue Aug 3 12:46:13 WAST 2010 
1: [%y%j] 
2: [%Y-%m-%d] 
3: [10162] 
DEBUG tm_sec = 0, tm_min = 0, tm_hour = 0, 
     tm_mday = 0, tm_mon = 0, tm_year = 0, 
     tm_wday = 0, tm_yday = 0, tm_isdst = 0 
DEBUG tm_sec = 0, tm_min = 0, tm_hour = 0, 
     tm_mday = 0, tm_mon = 0, tm_year = 110, 
     tm_wday = 0, tm_yday = 161, tm_isdst = 0 
retval = 0 
=: [2010-01-00] 

修復它是棘手的,我沒有意識到任何標準的時間函數將會重建從tm_yday獲得tm_mdaytm_mon

但是,如果你堅持一個解決方案,嘗試了這一點:

static void fixIt (struct tm *t) { 
    static int monthDaysN[] = {31,28,31,30,31,30,31,31,30,31,30,31}; 
    static int monthDaysL[] = {31,29,31,30,31,30,31,31,30,31,30,31}; 
    int *monthDays = monthDaysN; 
    int base = 0; 
    int i; 
    if (((t->tm_year + 1900) % 4) == 0) monthDays = monthDaysL; 
    if (((t->tm_year + 1900) % 100) == 0) monthDays = monthDaysN; 
    if (((t->tm_year + 1900) % 400) == 0) monthDays = monthDaysL; 
    // Leap years irrelevant for January dates. 
    if (t->tm_yday < 31) monthDays = monthDaysN; 
    for (i = 0; i < 12; i++) { 
     if (t->tm_yday - base < monthDays[i]) { 
      t->tm_mday = t->tm_yday - base + 1; 
      t->tm_mon = i; 
      return; 
     } 
     base += monthDays[i]; 
    } 
} 

它將爲基於tm_yeartm_yday這兩個領域,這是一個有點雜牌的,但將讓你在去最少(也許你會找到更好的方法)。

我會插入到該呼叫在您的轉換功能,只把它在特定環境下,以免覆蓋已設置的值:

int convertDateTime(char* source_fmt,char* dest_fmt,char* source,char* dest) { 
    struct tm tmpptr; 
    memset (&tmpptr,0,sizeof(tmpptr)); 
    dump(); 
    if (strptime(source,source_fmt,&tmpptr) == NULL) { 
      strcpy(dest,""); 
      return -1; 
    } 
    if ((tmpptr.tm_yday != 0) && (tmpptr.tm_mday == 0)) 
     fixIt (&tmpptr); 
    dump(); 
    strftime(dest,100,dest_fmt,&tmpptr); 
    return 0; 
} 

這給:

pax> date ; testprog %y%j %Y-%m-%d 10162 
Tue Aug 3 13:34:36 WAST 2010 
1: [%y%j] 
2: [%Y-%m-%d] 
3: [10162] 
DEBUG tm_sec = 0, tm_min = 0, tm_hour = 0, 
     tm_mday = 0, tm_mon = 0, tm_year = 0, 
     tm_wday = 0, tm_yday = 0, tm_isdst = 0 
DEBUG tm_sec = 0, tm_min = 0, tm_hour = 0, 
     tm_mday = 11, tm_mon = 5, tm_year = 110, 
     tm_wday = 0, tm_yday = 161, tm_isdst = 0 
retval = 0 
=: [2010-06-11] 

而且,像這裏的所有代碼一樣,您應該對其進行徹底測試。我很確定我得到了所有的邊緣情況,但是,既然你沒有爲我的服務付給我冷硬的現金,你應該認爲這只是一般性建議,而不是一個具體的解決方案:-)

+0

感謝您的建議(和時間):-)但我會去更簡單的解決方案.. – vineet 2010-08-03 07:14:11