Boost日期时间解析字符串

7

我看了很多例子,似乎都是为了解决这个简单的问题。 我想解析的字符串是:

"2012-06-01 16:45:34 EDT"

我尝试使用以下代码创建一个local_time_input_facet

"%Y-%m-%d %H:%M:%S %Z"

local_date_time对象的区域指针始终未设置。 阅读文档很令人困惑:

%Z *! 仅输出完整的时区名称。 当与ptime一起使用time_facet时,此标志将被忽略。

"EDT" // 东部夏令时

有人以前做过这个吗?

更新:我已经更新了代码,以更好地说明问题:

using namespace std;
using namespace boost::local_time;

int main()
{
    stringstream ss;

    // Set up the input datetime format.
    local_time_input_facet *input_facet 
    = new local_time_input_facet("%Y-%m-%d %H:%M:%S %ZP");
    ss.imbue(std::locale(ss.getloc(), input_facet));

    local_date_time ldt(not_a_date_time),ldt1(not_a_date_time);

    // Read a time into ldt
    ss.str("2012-06-01 17:45:34 EDT");
    ss >> ldt;

    ss.str("2012-06-01 17:45:34 CDT");
        ss >> ldt1;
        std::cerr << (ldt - ldt1).total_seconds() << std::endl;

    // Write the time to stdout.
    cout << "Full Time:\t"   << ldt.to_string() << endl;
    cout << "Local time:\t"  << ldt.local_time() << endl;
    cout << "Time zone:\t"   << ldt.zone_as_posix_string() << endl;
    cout << "Zone abbrev:\t" << ldt.zone_abbrev() << endl;
    cout << "Zone offset:\t" << ldt.zone_abbrev(true) << endl;

    cout << "Full Time:\t"   << ldt1.to_string() << endl;
    cout << "Local time:\t"  << ldt1.local_time() << endl;
    cout << "Time zone:\t"   << ldt1.zone_as_posix_string() << endl;
    cout << "Zone abbrev:\t" << ldt1.zone_abbrev() << endl;
    cout << "Zone offset:\t" << ldt1.zone_abbrev(true) << endl;

    return 0;
}

输出:

0
Full Time:  2012-Jun-01 17:45:34 EDT
Local time: 2012-Jun-01 17:45:34
Time zone:  EDT+00
Zone abbrev:    EDT
Zone offset:    +0000
Full Time:  2012-Jun-01 17:45:34 CDT
Local time: 2012-Jun-01 17:45:34
Time zone:  CDT+00
Zone abbrev:    CDT
Zone offset:    +0000
1个回答

7

错误

根据boost的文档http://www.boost.org/doc/libs/1_57_0/doc/html/date_time/date_time_io.html#date_time.format_flags

%Z是:

完整的时区名称(仅用于输出)。

它还说要用%ZP

Posix时区字符串(可供输入和输出使用)。

因此,您需要将%Z更改为%ZP。现在您的时间戳将被解析。但是,您会注意到时区偏移量将不会被设置。

Posix时区字符串

对于Posix时区字符串,您需要至少指定时区缩写和与UTC的偏移量,例如EST-5

完整的Posix时区字符串格式如下:

"std offset dst [offset],start[/time],end[/time]"

根据 http://www.boost.org/doc/libs/1_57_0/doc/html/date_time/local_time.html#date_time.local_time.posix_time_zone,时间区域字符串中不包含空格。

以下是EST和PST的完整Posix时区字符串示例:

EST-5EDT,M3.2.0,M11.1.0
PST-8PDT,M4.1.0,M10.1.0

这里包含了夏令时实行的信息。

然而,根据你所做的事情,你可能可以使用 EDT-4 ,但也有可能不行。

值得注意的是,尽管 Posix 时区很简单,但它们的局限性在于它们不能考虑历史上时区规则的变化。我认为最好一开始就避免处理时区。

如果我无法控制输入格式怎么办?

正如 OP 在评论中指出的那样,输入时间戳中没有列出偏移量。解决方案之一是从输入时间戳末尾读取区域缩写(例如 "EDT"),并将其与已知区域缩写的映射进行比对:

std::map<std::string, std::string> zone_map;
zone_map["EST"] = "EST-5EDT,M4.1.0,M10.5.0";  // Eastern Standard Time
zone_map["EDT"] = zone_map["EST"];            // Eastern Daylight Time
zone_map["PST"] = "PST-8PDT,M4.1.0,M10.1.0";  // Pacific Standard Time
zone_map["PDT"] = zone_map["PST"];            // Pacific Daylight Time
// ...

(请注意上述DST区域应与标准时区相同。)
(您也可以仅存储简单的UTC偏移量。)
zone_map["EST"] = "EST-5";  // Eastern Standard Time
zone_map["EDT"] = "EDT-4";  // Eastern Daylight Time
// ...

实际应用示例

下面是一个使用内置时区数据库的示例:

#include <map>
#include <string>
#include <sstream>
#include <iostream>
#include <boost/date_time/local_time/local_time.hpp>

using namespace boost::local_time;

int main()
{
    // A little database of time zones.
    std::map<std::string, std::string> zone_map;
    zone_map["EST"] = "EST-5EDT,M4.1.0,M10.5.0";  // Eastern Standard Time
    zone_map["EDT"] = zone_map["EST"];            // Eastern Daylight Time
    zone_map["PST"] = "PST-8PDT,M4.1.0,M10.1.0";  // Pacific Standard Time
    zone_map["PDT"] = zone_map["PST"];            // Pacific Daylight Time
    // ...

    // This is our input timestamp.
    std::string timestamp = "2012-06-01 16:45:34 EDT";

    // Replace time zone abbrev with full Posix time zone.
    const size_t abbrev_pos = timestamp.find_last_of(' ') + 1;
    const std::string abbrev = timestamp.substr(abbrev_pos);
    timestamp.replace(abbrev_pos, std::string::npos, zone_map[abbrev]);

    std::cout << "Time stamp with full timezone: " << timestamp << std::endl;

    // Set up the input datetime format.
    local_time_input_facet *input_facet = new local_time_input_facet("%Y-%m-%d %H:%M:%S %ZP");
    std::stringstream ss;
    ss.imbue(std::locale(ss.getloc(), input_facet));

    // This is our output date time.
    local_date_time ldt(not_a_date_time);

    // Read the timestamp into ldt.
    ss.str(timestamp);
    ss >> ldt;

    // Write the time to stdout.
    std::cout << "Full Time:\t"   << ldt.to_string() << std::endl
              << "Local time:\t"  << ldt.local_time() << std::endl
              << "Time zone:\t"   << ldt.zone_as_posix_string() << std::endl
              << "Zone abbrev:\t" << ldt.zone_abbrev() << std::endl
              << "Zone offset:\t" << ldt.zone_abbrev(true) << std::endl;

    return 0;
}

这将输出:
Time stamp with full timezone: 2012-06-01 16:45:34 EST-5EDT,M4.1.0,M10.5.0
Full Time:      2012-Jun-01 16:45:34 EDT
Local time:     2012-Jun-01 16:45:34
Time zone:      EST-05EDT+01,M4.1.0/02:00,M10.5.0/02:00
Zone abbrev:    EDT
Zone offset:    -0400

虽然这里的解决方案可能不是最理想的,但这是我能想到的唯一方法。


我还应该补充一点,当我准确地运行你的程序时,我得到了相同的输出,但是我的时区不同。 - user805547
你说得对。我刚刚编写了一个演示,并更改时区的结果是1338569134。我会看看能否解决它。 - Daniel Hanrahan
我刚刚发现输入的时区并不重要(尝试输入“2012-06-01 17:45:34 CUSTARD”,它也能读取)。 - Daniel Hanrahan
ZP选项似乎要求您按照以下格式设置时区:“std offset dst [offset],start[/time],end[/time]”,例如“EST-5EDT,M4.1.0,M10.5.0”。我知道这很丑陋。我还尝试过不使用boost而是使用linux strptime(),但仍然得到相同的结果。如果您使用“EDT-4”,则会得到不同的结果。很抱歉我无法提供更多帮助。您可能需要问另一个问题,比如“如何在boost date_time中读取带有时区缩写的日期”。 - Daniel Hanrahan
一定有更好的方法。我无法控制输入格式。 - user805547
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接