如何将带时区的文本日期时间转换为ATL/MFC CTime?
嘿,我来帮你搞定这个CTime转换的问题!你遇到的ParseDateTime()失败,大概率是因为COleDateTime的这个方法对带时区偏移和缩写的格式支持不太友好,尤其是这种Sat, 13 Jan 2018 07:54:39 -0500 (EST)的标准RFC格式,咱们可以用两种靠谱的方案来解决:
方案1:用ATL的CAtlDateTimeParser(简单快捷)
ATL的CAtlDateTimeParser对各种日期时间格式支持更灵活,包括带时区偏移的情况。你只需要先清理掉字符串里的冗余部分(比如末尾的(EST)),然后指定匹配的格式字符串就能解析:
#include <atldatetime.h> CTime DateTimeString2CTime(CString DateTimeStr) { // 先移除末尾的时区缩写括号部分,比如 "(EST)" int parenPos = DateTimeStr.Find(_T(" (")); if (parenPos != -1) { DateTimeStr = DateTimeStr.Left(parenPos).Trim(); } CAtlDateTimeParser parser; CAtlDateTime dt; // 尝试匹配带星期的格式:"Sat, 13 Jan 2018 07:54:39 -0500" if (parser.Parse(DateTimeStr, dt, _T("%a, %d %b %Y %H:%M:%S %z"))) { return CTime(dt.GetYear(), dt.GetMonth(), dt.GetDay(), dt.GetHour(), dt.GetMinute(), dt.GetSecond()); } // 尝试不带星期的格式:"13 Jan 2018 07:54:39 -0500" if (parser.Parse(DateTimeStr, dt, _T("%d %b %Y %H:%M:%S %z"))) { return CTime(dt.GetYear(), dt.GetMonth(), dt.GetDay(), dt.GetHour(), dt.GetMinute(), dt.GetSecond()); } // 兼容GMT/UTC格式:"13 Jan 2018 07:54:39 GMT" if (parser.Parse(DateTimeStr, dt, _T("%d %b %Y %H:%M:%S GMT"))) { return CTime(dt.GetYear(), dt.GetMonth(), dt.GetDay(), dt.GetHour(), dt.GetMinute(), dt.GetSecond()); } // 解析失败返回无效CTime(默认构造的CTime代表1970-01-01 00:00:00 UTC) return CTime(); }
方案2:手动解析字符串(兼容性拉满,不依赖ATL)
如果你的项目不想依赖ATL,手动解析是更稳妥的选择,完全可控,不受系统区域设置影响:
CTime DateTimeString2CTime(CString DateTimeStr) { // 1. 清理字符串:移除开头的星期和末尾的时区缩写括号 DateTimeStr.Trim(); int commaPos = DateTimeStr.Find(_T(",")); if (commaPos != -1) { DateTimeStr = DateTimeStr.Mid(commaPos + 1).Trim(); } int parenPos = DateTimeStr.Find(_T(" (")); if (parenPos != -1) { DateTimeStr = DateTimeStr.Left(parenPos).Trim(); } // 2. 拆分日期时间部分和时区部分 CString dateTimePart, tzPart; int lastSpacePos = DateTimeStr.ReverseFind(_T(' ')); if (lastSpacePos != -1) { dateTimePart = DateTimeStr.Left(lastSpacePos).Trim(); tzPart = DateTimeStr.Mid(lastSpacePos + 1).Trim(); } else { dateTimePart = DateTimeStr; tzPart = _T("0000"); // 默认按UTC处理 } // 3. 解析日期时间字段:"13 Jan 2018 07:54:39" int day, year, hour, min, sec; TCHAR monthAbbr[4]; if (_stscanf_s(dateTimePart, _T("%d %3s %d %d:%d:%d"), &day, monthAbbr, _countof(monthAbbr), &year, &hour, &min, &sec) != 6) { return CTime(); // 解析失败返回无效时间 } // 4. 月份缩写转数字 static const struct { LPCTSTR abbr; int month; } monthMap[] = { {_T("Jan"), 1}, {_T("Feb"), 2}, {_T("Mar"), 3}, {_T("Apr"), 4}, {_T("May"), 5}, {_T("Jun"), 6}, {_T("Jul"), 7}, {_T("Aug"), 8}, {_T("Sep"), 9}, {_T("Oct"), 10}, {_T("Nov"), 11}, {_T("Dec"), 12} }; int month = 0; for (int i = 0; i < _countof(monthMap); ++i) { if (_tcsicmp(monthAbbr, monthMap[i].abbr) == 0) { month = monthMap[i].month; break; } } if (month == 0) { return CTime(); } // 5. 处理时区偏移,转换为UTC时间(如需本地时间可调用UTCToLocal()) int tzHours = 0, tzMins = 0; if (tzPart.GetLength() == 5) { // 处理偏移格式:"-0500"或"+0800" TCHAR sign = tzPart[0]; _stscanf_s(tzPart.Mid(1), _T("%2d%2d"), &tzHours, &tzMins); if (sign == _T('-')) { tzHours = -tzHours; tzMins = -tzMins; } } else { // 处理时区缩写:EST/GMT/UTC等 static const struct { LPCTSTR tz; int offsetHours; } tzMap[] = { {_T("EST"), -5}, {_T("EDT"), -4}, {_T("CST"), -6}, {_T("CDT"), -5}, {_T("MST"), -7}, {_T("MDT"), -6}, {_T("PST"), -8}, {_T("PDT"), -7}, {_T("GMT"), 0}, {_T("UTC"), 0} }; for (int i = 0; i < _countof(tzMap); ++i) { if (_tcsicmp(tzPart, tzMap[i].tz) == 0) { tzHours = tzMap[i].offsetHours; tzMins = 0; break; } } } // 构造时间并调整时区:原时间是带时区的,转UTC需要抵消偏移 CTime temp(year, month, day, hour, min, sec); temp += CTimeSpan(tzHours, tzMins, 0, 0); // 如果需要转换为本地时间,取消下面注释 // temp = temp.UTCToLocal(); return temp; }
注意点
CTime本身内部存储的是UTC时间戳,调用GetHour()等方法时会自动转换为本地时区时间(除非你用GetUTC*()方法)。- 如果你的需求是直接保留原字符串中的时分秒(不做时区转换),可以跳过方案2中的时区偏移调整步骤,直接返回构造的
CTime(year, month, day, hour, min, sec)。
内容的提问来源于stack exchange,提问作者Michael Haephrati
相关产品推荐
相关产品推荐

