首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >strftime/strptime问题

strftime/strptime问题
EN

Stack Overflow用户
提问于 2010-08-03 11:41:04
回答 3查看 4.8K关注 0票数 0

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

代码语言:javascript
复制
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;julian day不起作用。

我在Solaris10上使用的是gcc,你知道我需要修改什么吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-08-03 14:25:33

更新

原始答案(如下)假定在输出(strftime)而不是输入(strptime)上使用"%y%j“格式。mktime函数将根据有效信息计算yday,但它不能以另一种方式工作。

如果你想解码这样的东西,你需要手动完成。下面是一些示例代码。它只进行了最少的测试,而且几乎肯定有bug。您可能需要更改ir,这取决于您想要的输入格式。

代码语言:javascript
复制
#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 2007年5月03日星期四08123 2008年5月02日星期五08124星期六

结束更新

在调用strptime()之后,调用mktime(),它将填充结构中任何缺失的成员。此外,您应该在开始之前将结构清零。

代码语言:javascript
复制
#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;
}
票数 -1
EN

Stack Overflow用户

发布于 2010-08-03 12:30:26

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

这一直是我在MacOS上遇到的一个问题,在10.5中,随着UNIX标准遵从性的努力,实现发生了变化。在此之前,下面的调用运行良好。

代码语言:javascript
复制
strptime("20071124", "%Y%m%d")

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

代码语言:javascript
复制
#define _NONSTD_SOURCE

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

票数 0
EN

Stack Overflow用户

发布于 2010-08-03 12:45:38

我可以告诉你问题出在哪里。当您将strptime()%j格式一起使用时,它仅填充struct tm上的tm_yday字段。顺便说一下,这并不局限于Solaris,CygWin gcc也在做同样的事情。

因为您的strftime()最有可能使用其他字段(tm_montm_mday),而这些字段仍然设置为零,这就是为什么您得到了错误的一年中的日期。

下面的代码说明了这一点:

代码语言:javascript
复制
#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;
}

当你这样运行它时:

代码语言:javascript
复制
pax> date ; ./tetsprog %y%j %Y-%m-%d 10162

你会得到:

代码语言:javascript
复制
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

但是,如果你被困在一个解决方案中,试试这个:

代码语言:javascript
复制
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设置这两个字段,这有点麻烦,但至少可以让您继续操作(也许您会找到更好的方法)。

我会在您的convert函数中插入对this的调用,并仅在特定情况下调用它,以免覆盖已设置的值:

代码语言:javascript
复制
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;
}

这就给出了:

代码语言:javascript
复制
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
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3393434

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档