使用 boost/std 库来解析带时区的日期

Using boost/std libraries to parse date with timezone

我想将输入日期字符串(输入中未提供时间)(例如“2017-05-04”)转换为 unix 时间戳,时间为“00:00:01”,时区为太平洋时间( 'America/Los_Angeles).

我不太确定如何执行此操作 - 我使用 boost::posix_time::time_from_string 进行了探索,但它似乎无法处理时区。 任何建议将不胜感激!

使用 Howard Hinnant's free, open source, C++11/14/17 timezone library,您可以使用以下语法执行此操作:

#include "tz.h"
#include <cassert>
#include <iostream>
#include <sstream>
#include <string>

date::sys_seconds
my_parse(const std::string& in)
{
    using namespace std;
    using namespace std::chrono;
    using namespace date;
    local_seconds ls;
    istringstream infile{in};
    infile >> parse("%F", ls);
    assert(!infile.fail());
    return make_zoned("America/Los_Angeles", ls + seconds{1}).get_sys_time();
}

int
main()
{
    std::cout << my_parse("2017-05-04").time_since_epoch().count() << '\n';
}

这个程序输出:

1493881201

使用诸如 http://www.convert-unix-time.com/?t=1493881201 之类的网站,您可以确认 1493881201 对应于 2017 年 5 月 4 日星期四 07:00:01 AM UTC,即 2017 年 5 月 4 日星期四 00:00:01 AM PDT。

使用 boost,解析 posix::date_time(或 gregorian::date)并在构建 local_date_time 对象时添加时间和时区。

这是一个演示 - 甚至没有使用输入方面,因为老实说,在这里手动解析它似乎更简单:

Live On Coliru

#include <boost/date_time/local_time/local_time.hpp>
#include <boost/lexical_cast.hpp>
#include <iostream>

struct MyTime {
    static boost::local_time::time_zone_ptr time_zone() {
        using namespace boost::local_time;

        static auto zone = [] { // one-time initialization
            tz_database db;
            //libs/date_time/data/date_time_zonespec.csv is included with boost
            std::istringstream fake_db(R"("America/Los_Angeles","PST","Pacific Standard Time","PDT","Pacific Daylight Time","-08:00:00","+01:00:00","2;0;3","+02:00:00","1;0;11","+02:00:00")");
            db.load_from_stream(fake_db);
            return db.time_zone_from_region("America/Los_Angeles");
        }();

        return zone;
    }

    boost::local_time::local_date_time _value { boost::date_time::not_a_date_time, time_zone() };

    friend std::istream& operator>>(std::istream& is, MyTime& parsed) {
        unsigned short y, m, d;
        char delim;
        if (is 
                >> std::noskipws // optionally of course
                && is >> y && (is >> delim && delim == '-')
                && is >> m && (is >> delim && delim == '-')
                && is >> d)
        {
            using namespace boost::local_time;
            local_date_time ldt({y, m, d}, {0,0,1}, time_zone(), true);
            parsed._value = ldt;
            return is;
        }

        is.setstate(is.rdstate() | std::ios::failbit);
        return is;
    }

    friend std::ostream& operator<<(std::ostream& os, MyTime const& v) {
        return os << v._value;
    }
};

int main()
{
    {
        MyTime dt;

        std::cout << "Not parsed yet: " << dt << "\n";

        dt = boost::lexical_cast<MyTime>("2017-05-04");
        std::cout << "Parsed: " << dt << " (base utc offset: " << dt._value.zone()->base_utc_offset() << ")\n";
    }

    // errors
    for (auto err : { "2017-15-04", "3", "", "17-1-77", "2017/05/04", "2017-05-04 x" }) try {
        MyTime dt;
        dt = boost::lexical_cast<MyTime>(err);
        std::cout << "Should not have parsed: " << dt << "\n";
    } catch(std::exception const& e) {
        std::cerr << "'" << err << "': " << e.what() << "\n";
    }
}

打印:

Not parsed yet: not-a-date-time
Parsed: 2017-May-04 00:00:01 PDT (base utc offset: -08:00:00)
'2017-15-04': Month number is out of range 1..12
'3': bad lexical cast: source type value could not be interpreted as target
'': bad lexical cast: source type value could not be interpreted as target
'17-1-77': Year is out of valid range: 1400..10000
'2017/05/04': bad lexical cast: source type value could not be interpreted as target
'2017-05-04 x': bad lexical cast: source type value could not be interpreted as target

要使用完整的时区数据库,请将 timezone() 更改为

static auto zone = [] { // one-time initialization
    tz_database db;
    db.load_from_file("/home/sehe/custom/boost_1_62_0/libs/date_time/data/date_time_zonespec.csv");
    return db.time_zone_from_region("America/Los_Angeles");
}();

Point to the location where your boost library is installed, or where you deploy a copy of that database

我也不喜欢使用自定义库,但 Howard Hinnant 的库比 boost/date_time 好得多,它取决于用户提供的“.csv”文件。 HH 的图书馆直接从本地时区存储库收集信息,这些信息由系统频繁更新,不需要您维护。 HH 也是 std++ 的提案,我不知道为什么还没有实现。