将文本日期和时间转换为ATL :: CTime

时间:2018-01-13 22:47:27

标签: c++ winapi mfc atl ctime

给定文本日期和时间,例如:

Sat, 13 Jan 2018 07:54:39 -0500 (EST)

如何将其转换为ATL / MFC CTime

该函数应该返回(在我的例子中):

CTime(2018,1,13,7,54,39) 

以GMT / UTF或plus the time zone

更新

我尝试编写以下函数,但似乎ParseDateTime()总是失败。

CTime DateTimeString2CTime(CString DateTimeStr)
{
    COleDateTime t;
    if (t.ParseDateTime(DateTimeStr))
    {
        CTime result(t);
        return result;
    }
    return (CTime)NULL;
}

2 个答案:

答案 0 :(得分:3)

您必须将字符串解析为各个时间组件,将它们转换为整数并将它们传递给相应的CTime构造函数。

解析有很多种方法,其中一种最简单易用的方法是使用regular expressions(一旦习惯了语法):

#include <iostream>
#include <regex>

void test( std::wstring const& s, std::wregex const& r );

int main()
{
    std::wregex const r{ 
        LR"(.*?)"            // any characters (none or more) 
        LR"((\d+))"          // match[1] = day
        LR"(\s*)"            // whitespace (none or more)
        LR"((Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))"  // match[2] = month 
        LR"(\s*)"            // whitespace (none or more)
        LR"((\d+))"          // match[3] = year
        LR"(\s+)"            // whitespace (1 or more)
        LR"((\d+))"          // match[4] = hour
        LR"(\s*:\s*)"        // whitespace (none ore more), colon (1), whitespace (none ore more)
        LR"((\d+))"          // match[5] = minute
        LR"((?:\s*:\s*(\d+))?)" // match[6] = second (none or more) 
        LR"(.*)"             // any characters (none or more)
        , std::regex_constants::icase };

    test( L"Sat, 13 Jan 2018 07:54:39 -0500 (EST)", r );
    test( L"Wed, 10 jan2018 18:30 +0100", r );
    test( L"10Jan 2018 18 :30 : 00 + 0100", r );
}

void test( std::wstring const& s, std::wregex const& r )
{
    std::wsmatch m;
    if( regex_match( s, m, r ) )
    {
        std::wcout 
            << L"Day    : " << m[ 1 ] << L'\n'
            << L"Month  : " << m[ 2 ] << L'\n'
            << L"Year   : " << m[ 3 ] << L'\n'
            << L"Hour   : " << m[ 4 ] << L'\n'
            << L"Minute : " << m[ 5 ] << L'\n'
            << L"Second : " << m[ 6 ] << L'\n';
    }
    else
    {
        std::wcout << "no match" << '\n';    
    }
    std::wcout << std::endl;
}

Live demo.

指定括号中每个组件的模式(r变量)。调用regex_match后,结果存储在变量m中,您可以通过下标运算符访问每个组件(也称为子匹配)。这些也是std::wstring

如有必要,捕获可以由正则表达式库以及std::stoi抛出的异常。为简洁起见,我省略了这段代码。

修改

在OP评论说需要更强大的解析之后,我相应地修改了正则表达式。 从调用test()函数可以看出,空白要求现在更加宽松。此外,时间戳的部分现在是可选的。这是使用(?:引入并以)结尾的非捕获组实现的。通过在该组之后放置?,整个组(包括空格,:和数字)可以无一次或一次出现,但只捕获数字。

注意: LR"()"指定raw string literal以使正则表达式更具可读性(避免转义反斜杠)。所以外括号不是实际正则表达式的一部分!

对于手动解析,可以使用std::wstringstream。在我看来,与正则表达式相比,唯一的优势是可以获得更好的性能。否则,此解决方案难以维护,例如,如果将来必须更改时间格式。

#include <iostream>
#include <sstream>
#include <array>
#include <string>

int month_to_int( std::wstring const& m )
{
    std::array<wchar_t const*, 12> names{ L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec" };
    for( std::size_t i = 0; i < names.size(); ++i )
    {
        if( names[ i ] == m )
            return i + 1;
    }
    return 0;
}

int main()
{
    std::wstringstream s{ L"Sat, 13 Jan 2018 07:54:39 -0500 (EST)" };
    std::wstring temp;
    int day, month, year, hour, minute, second;
    // operator >> reads until whitespace delimiter
    s >> temp;
    s >> day;
    s >> temp; month = month_to_int( temp );
    s >> year;
    // use getline to explicitly specify the delimiter
    std::getline( s, temp, L':' ); hour = std::stoi( temp );
    std::getline( s, temp, L':' ); minute = std::stoi( temp );
    // last token separated by whitespace again
    s >> second;

    std::cout 
        << "Day    : " << day << '\n'
        << "Month  : " << month << '\n'
        << "Year   : " << year << '\n'
        << "Hour   : " << hour << '\n'
        << "Minute : " << minute << '\n'
        << "Second : " << second << '\n';
}

Live demo.

同样,为简洁起见,此处没有错误处理。您应该在每次输入操作后检查流状态,或者在构造之后调用std::wstringstream::exceptions()以启用异常并处理它们。

答案 1 :(得分:3)

作为手动解析的替代方法,您可以使用COleDateTime Class及其成员COleDateTime::ParseDateTime

bool ParseDateTime(  
 LPCTSTR lpszDate,
 DWORD dwFlags = 0,
 LCID lcid = LANG_USER_DEFAULT) throw();

来自文档:

The lpszDate parameter can take a variety of formats.
For example, the following strings contain acceptable date/time formats:

"25 January 1996"
"8:30:00"
"20:30:00"
"January 25, 1996 8:30:00"
"8:30:00 Jan. 25, 1996"
"1/25/1996 8:30:00" // always specify the full year,
// even in a 'short date' format

如果需要,您可以从那里转换为CTime