代码之家  ›  专栏  ›  技术社区  ›  Loki Astari

C++将时间字符串转换为秒

  •  11
  • Loki Astari  · 技术社区  · 14 年前

    我有一个字符串,格式如下:

    2010-11-04T23:23:01Z


    我宁愿把它当作一个划时代的时间来比较。

    目前(在quck搜索之后),simplist算法是:

    1: <Convert string to struct_tm: by manually parsing string>
    2: Use mktime() to convert struct_tm to epoch time.
    
    // Problem here is that mktime uses local time not UTC time.
    
    10 回复  |  直到 12 年前
        1
  •  10
  •   Mayur    6 年前

    使用C++ 11的功能,我们现在可以使用流解析时间:

    伊曼尼普 std::get_time 将基于一组格式参数转换字符串并将其转换为 struct tz

    你可以用 std::mktime() 将其转换为纪元值。

    #include <iostream>
    #include <sstream>
    #include <locale>
    #include <iomanip>
    
    int main()
    {
        std::tm t = {};
        std::istringstream ss("2010-11-04T23:23:01Z");
    
        if (ss >> std::get_time(&t, "%Y-%m-%dT%H:%M:%S"))
        {
            std::cout << std::put_time(&t, "%c") << "\n"
                      << std::mktime(&t) << "\n";
        }
        else
        {
            std::cout << "Parse failed\n";
        }
        return 0;
    }
    
        2
  •  6
  •   Kirill V. Lyadvinsky    14 年前

    这是ISO8601格式。你可以用 strptime 函数来解析它 %FT%T%z this ,例如)。

        3
  •  4
  •   Justin Ethier    14 年前

    您可以使用如下函数 strptime 将字符串转换为 struct tm

        4
  •  3
  •   Community Ramakrishna.p    7 年前

    这不是一个确切的重复,但你可以从 here 我敢打赌,这很有用。这特别假定UTC输入。

    Boost还支持通过 boost::posix_time::from_iso_string boost::date_time::parse_iso_time ,这里您只需去掉后面的'Z'并将TZ视为隐式UTC。

    #include <iostream>
    #include <boost/date_time.hpp>
    
    namespace bt = boost::posix_time;
    
    const std::locale formats[] = {
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d %H:%M:%S")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y/%m/%d %H:%M:%S")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%d.%m.%Y %H:%M:%S")),
    std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d"))};
    const size_t formats_n = sizeof(formats)/sizeof(formats[0]);
    
    std::time_t pt_to_time_t(const bt::ptime& pt)
    {
        bt::ptime timet_start(boost::gregorian::date(1970,1,1));
        bt::time_duration diff = pt - timet_start;
        return diff.ticks()/bt::time_duration::rep_type::ticks_per_second;
    
    }
    void seconds_from_epoch(const std::string& s)
    {
        bt::ptime pt;
        for(size_t i=0; i<formats_n; ++i)
        {
            std::istringstream is(s);
            is.imbue(formats[i]);
            is >> pt;
            if(pt != bt::ptime()) break;
        }
        std::cout << " ptime is " << pt << '\n';
        std::cout << " seconds from epoch are " << pt_to_time_t(pt) << '\n';
    }
    int main()
    {
        seconds_from_epoch("2004-03-21 12:45:33");
        seconds_from_epoch("2004/03/21 12:45:33");
        seconds_from_epoch("23.09.2004 04:12:21");
        seconds_from_epoch("2003-02-11");
    }
    
        5
  •  3
  •   Mark Lakata    8 年前

    Linux提供 timegm 这就是你想要的(即mktime表示UTC时间)。

    这是我的解决方案,我被迫只接受“Zulu”(Z时区)。注意 strptime 实际上似乎没有正确解析时区,甚至 尽管glib似乎对此有些支持。所以我就扔了一个 如果字符串不以“Z”结尾,则出现异常。

    static double EpochTime(const std::string& iso8601Time)
    {
        struct tm t;
        if (iso8601Time.back() != 'Z') throw PBException("Non Zulu 8601 timezone not supported");
        char* ptr = strptime(iso8601Time.c_str(), "%FT%T", &t);
        if( ptr == nullptr)
        {
            throw PBException("strptime failed, can't parse " + iso8601Time);
        }
        double t2 = timegm(&t); // UTC
        if (*ptr)
        {
            double fraction = atof(ptr);
            t2 += fraction;
        }
        return t2;
    }
    
        6
  •  2
  •   Howard Hinnant    8 年前

    旧问题的新答案。新答案的基本原理:如果您想使用 <chrono>

    除了C++ 11/C++ 14之外,你还需要这个 free, open source date/time library :

    #include "tz.h"
    #include <iostream>
    #include <sstream>
    
    int
    main()
    {
        std::istringstream is("2010-11-04T23:23:01Z");
        is.exceptions(std::ios::failbit);
        date::sys_seconds tp;
        date::parse(is, "%FT%TZ", tp);
        std::cout << "seconds from epoch is " << tp.time_since_epoch().count() << "s\n";
    }
    

    该程序输出:

    seconds from epoch is 1288912981s
    

    如果解析以任何方式失败,将引发异常。如果你不想抛出异常,就不要 is.exceptions(std::ios::failbit); is.fail() .

        7
  •  1
  •   Yippie-Ki-Yay    14 年前

    你可以利用 boost::date_time 编写一个小型的手动解析器 (可能基于regexp)

        8
  •  1
  •   casablanca    14 年前

    这里的问题是mktime使用本地时间而不是UTC时间。

    不如直接计算UTC和本地时间之间的时差,然后将其添加到 mktime ?

    time_t local = time(NULL),
           utc   = mktime(gmtime(&local));
    int    diff  = utc - local;
    
        9
  •  1
  •   Dirk is no longer here    14 年前

    strptime() ?

    在Linux上,您甚至可以得到“seconds east of UTC”字段,从而无需解析:

    #define _XOPEN_SOURCE
    #include <iostream>
    #include <time.h>
    
    int main(void) {
    
        const char *timestr = "2010-11-04T23:23:01Z";
    
        struct tm t;
        strptime(timestr, "%Y-%m-%dT%H:%M:%SZ", &t);
    
        char buf[128];
        strftime(buf, sizeof(buf), "%d %b %Y %H:%M:%S", &t);
    
        std::cout << timestr << " -> " << buf << std::endl;
    
        std::cout << "Seconds east of UTC " << t.tm_gmtoff << std::endl;
    }   
    

    /tmp$ g++ -o my my.cpp 
    /tmp$ ./my
    2010-11-04T23:23:01Z -> 04 Nov 2010 23:23:01
    Seconds east of UTC 140085769590024
    
        10
  •  0
  •   caf    14 年前

    X/Open提供了一个全局 timezone 变量,指示本地时间落后于UTC的秒数。你可以用这个来调整 mktime() :

    #define _XOPEN_SOURCE
    #include <stdio.h>
    #include <time.h>
    
    /* 2010-11-04T23:23:01Z */
    time_t zulu_time(const char *time_str)
    {
        struct tm tm = { 0 };
    
        if (!strptime(time_str, "%Y-%m-%dT%H:%M:%SZ", &tm))
            return (time_t)-1;
    
        return mktime(&tm) - timezone;
    }
    
    推荐文章