C ++将时间字符串转换为纪元的秒数

时间:2010-11-09 19:41:17

标签: c++ c time platform-independent

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

  

2010-11-04T23:23:01Z

Z表示时间是UTC 我宁愿将其存储为一个时间点,以便比较容易。

这样做的推荐方法是什么?

目前(在quck搜索之后)简化算法是:

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 个答案:

答案 0 :(得分:9)

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

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

答案 1 :(得分:6)

这是ISO8601格式。您可以使用strptime函数使用%FT%T%z参数对其进行解析。它不是C ++标准的一部分,但您可以使用它的开源实现(例如this)。

答案 2 :(得分:4)

您可以使用strptime之类的函数将字符串转换为struct tm,而不是手动解析。

答案 3 :(得分:3)

这不是一个确切的重复,但你会发现@ Cubbi的答案来自here有用,我打赌。这特别假设UTC输入。

Boost还支持通过调用boost::posix_time::from_iso_string的{​​{1}}从ISO 8601直接转换,在这里你只需删除尾随的'Z'并将TZ视为隐式UTC。

boost::date_time::parse_iso_time

答案 4 :(得分:3)

  

问题在于mktime使用本地时间而不是UTC时间。

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

这是我的解决方案,我不得不接受&#34; Zulu&#34; (Z时区)。注意 strptime实际上似乎并没有正确解析时区 虽然glib似乎对此有一些支持。这就是为什么我只是抛出一个 如果字符串没有以&#39; Z&#39;

结尾,则会出现异常
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;
}

答案 5 :(得分:2)

旧问题的新答案。新答案的基本原理:如果您想使用<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()

答案 6 :(得分:1)

您可以使用boost::date_time并为您的字符串编写一个小的手动解析器(可能是基于regexp的)

答案 7 :(得分:1)

  

问题在于mktime使用本地时间而不是UTC时间。

如何计算UTC与本地时间之间的时差,然后将其添加到mktime返回的值?

time_t local = time(NULL),
       utc   = mktime(gmtime(&local));
int    diff  = utc - local;

答案 8 :(得分:1)

strptime()出了什么问题?

在Linux上,您甚至可以获得“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

答案 9 :(得分:0)

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