strftime/strptime问题

发布于 2024-09-13 06:59:20 字数 486 浏览 6 评论 0 原文

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


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。知道我需要更改什么吗?

I've written a function to convert datetimes from one format to another -


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

It works fine for most formats, But when I use format = "%y%j", all I get is 10001; the julian day does not work.

I'm using gcc on solaris 10. Any idea what i need to change?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

层林尽染 2024-09-20 06:59:20

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

这对我来说在 MacOS 上是个问题,随着 UNIX 标准合规性的努力,实现在 10.5 中发生了变化。在此之前,以下调用运行良好。

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

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

#define _NONSTD_SOURCE

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

Check the expected behaviour of strptime, in some implementations it is greedy and will consume as many digits as it can.

This has been a problem for me on MacOS, the implementation changed in 10.5 with the UNIX standards compliance efforts. Before this the following call worked fine.

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

As of 10.5 you have to do the following to get it to work.

#define _NONSTD_SOURCE

Your compiler libraries and OS may differ though.

顾挽 2024-09-20 06:59:20

我可以告诉你问题是什么。当您使用 %j 格式的 strptime() 时,它仅填充 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_mdaytm_mon来自tm_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_year 和 tm_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]

并且,像这里的所有代码一样,您应该彻底测试它。我很确定我得到了所有的边缘情况,但是,由于您没有为我的服务支付冷硬现金,您应该假设这只是一般建议,而不是特定的解决方案:-)

I can tell you what the problem is. When you use strptime() with the %j format, it only populates the tm_yday field on your struct tm. This isn't limited to Solaris by the way, CygWin gcc is doing the same thing.

Because your strftime() is most likely using other fields (tm_mon and tm_mday), and these are still set to zero, that's why you're getting the wrong day of the year.

The following code illustrates this:

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

When you run it thus:

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

you get:

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]

Fixing it is tricky, I'm not aware of any standard time function that will rebuild tm_mday and tm_mon from tm_yday.

But, if you're stuck for a solution, try this out:

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

It will set those two fields based on tm_year and tm_yday and it's a bit of a kludge but will get you going at least (and maybe you'll find a better way).

I would insert a call to this in your convert function and only call it under specific circumstances so as not to overwrite values that are already set:

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

which gives:

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]

And, like all code here, you should test it thoroughly. I'm pretty certain I got all the edge cases but, since you haven't paid me cold hard cash for my services, you should assume this is general advice only, not a specific solution :-)

蓬勃野心 2024-09-20 06:59:20

更新
原始答案(如下)假设“%y%j”格式用于输出(strftime)而不是输入(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 星期四, 2007 年 5 月 3 日
08123 2008 年 5 月 2 日星期五
08124 2008 年 5 月 3 日星期六

结束更新

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

Update
The original answer (below) presumed that the "%y%j" format was used on the output (strftime) not the input (strptime). The mktime function will compute yday from valid info, but it doesn't work the other way.

If you want to decode something like that you need to do it manually. Some example code follows. It has had minimal testing and almost certainly has bugs. You may need to alter ir, depending in the input format you want.

#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

End Update

After you call strptime(), call mktime() which will populate any missing members of the struct. Also, you should zero out the struct before beginning.

#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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文