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



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

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

如何将其转换为 ATL/MFCCTime

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

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

格林威治标准时间/UTF 或加时区

更新:

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

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

作为手动解析的替代方法,您可以使用 COLEDateTime 类,它是成员 COLEDateTime::P arseDateTime:

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

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

解析的方法有很多种,其中最直接和易于维护的方法之一是使用正则表达式(一旦你习惯了语法):

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

现场演示。

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

如有必要,捕获正则表达式库可能引发的异常以及std::stoi。为了简洁起见,我省略了此代码。

编辑

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

注意:LR"()"指定一个原始字符串文字以使正则表达式更具可读性(它避免了转义反斜杠)。所以外括号不是实际正则表达式的一部分!


对于手动解析,可以使用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';
}

现场演示。

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

最新更新