strftime / strptime问题

时间:2010-08-03 03:41:04

标签: c++ solaris

我编写了一个将日期时间从一种格式转换为另一种格式的函数 -


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;
}

它适用于大多数格式,但当我使用format =“%y%j”时,我得到的只是10001;朱利安日不起作用。

我在solaris 10上使用gcc。知道我需要改变什么吗?

3 个答案:

答案 0 :(得分:0)

检查strptime的预期行为,在某些实现中它是贪婪的并且会消耗尽可能多的数字。

对于我来说,这对MacOS来说是一个问题,10.5的实现在UNIX标准合规性方面有所改变。在此之前,以下调用工作正常。

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

从10.5开始,您必须执行以下操作才能使其正常工作。

#define _NONSTD_SOURCE

您的编译器库和操作系统可能会有所不同。

答案 1 :(得分:0)

我可以告诉你问题是什么。当您使用strptime()格式的%j时,它只会填充tm_yday上的struct tm字段。顺便说一下,这不仅限于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_mday重建tm_montm_yday

但是,如果您遇到解决方案,请尝试一下:

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来设置这两个字段,而且它有点像kludge,但至少可以帮助你(也许你会找到更好的方法)。

我会在你的转换函数中插入一个调用,并且只在特定情况下调用它,以免覆盖已设置的值:

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]

而且,就像这里的所有代码一样,你应该彻底测试它。我很确定我得到了所有优势,但是,既然你没有向我支付冷硬现金用于我的服务,你应该假设这只是一般建议,而不是特定的解决方案: - )

答案 2 :(得分:-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;
}