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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4137748/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 14:37:28  来源:igfitidea点击:

C++ Converting a time string to seconds from the epoch

c++ctimeplatform-independent

提问by Martin York

I have a string with the following format:

我有一个具有以下格式的字符串:

2010-11-04T23:23:01Z

2010-11-04T23:23:01Z

The Z indicates that the time is UTC.
I would rather store this as a epoch time to make comparison easy.

Z 表示时间是 UTC。
我宁愿将其存储为纪元时间,以便于比较。

What is the recomended method for doing this?

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

Currently (after a quck search) the simplist algorithm is:

目前(经过快速搜索)最简单的算法是:

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.

采纳答案by Martin York

Using C++11 functionality we can now use streams to parse times:

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

The iomanip std::get_timewill convert a string based on a set of format parameters and convert them into a struct tzobject.

iomanipstd::get_time将根据一组格式参数转换字符串并将它们转换为struct tz对象。

You can then use std::mktime()to convert this into an epoch value.

然后,您可以使用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;
}

回答by Kirill V. Lyadvinsky

This is ISO8601 format. You can use strptimefunction to parse it with %FT%T%zargument. It is not a part of the C++ Standard though you can use open source implementation of it (this, for instance).

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

回答by Justin Ethier

You can use a function such as strptimeto convert a string to a struct tm, instead of parsing it manually.

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

回答by Steve Townsend

It's not an exact dup but you will find @Cubbi's answer from hereuseful, I wager. This specifically assumes UTC input.

这不是一个确切的重复,但你会发现@Cubbi 在这里的回答很有用,我敢打赌。这特别假设 UTC 输入。

Boost also support direct conversion from ISO 8601 via boost::posix_time::from_iso_stringwhich calls boost::date_time::parse_iso_time, here again you would just strip the trailing 'Z' and treat the TZ as implicit UTC.

Boost 还支持从 ISO 8601 直接转换,通过boost::posix_time::from_iso_stringwhich 调用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");
}

回答by Mark Lakata

Problem here is that mktime uses local time not UTC time.

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

Linux provides timegmwhich is what you want (i.e. mktime for UTC time).

Linux 提供timegm了您想要的(即 UTC 时间的 mktime)。

Here is my solution, which I forced to only accept "Zulu" (Z timezone). Note that strptimedoesn't actually seem to parse the time zone correctly, even though glib seems to have some support for that. That is why I just throw an exception if the string doesn't end in 'Z'.

这是我的解决方案,我被迫只接受“祖鲁语”(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;
}

回答by Howard Hinnant

New answer to an old question. Rationale for new answer: In case you want to use <chrono>types to solve a problem like this.

老问题的新答案。新答案的基本原理:如果您想使用<chrono>类型来解决这样的问题。

In addition to C++11/C++14, you'll need this free, open source date/time library:

除了 C++11/C++14,您还需要这个免费的开源日期/时间库

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

This program outputs:

该程序输出:

seconds from epoch is 1288912981s

If the parse fails in any way, an exception will be thrown. If you would rather not throw exceptions, don't is.exceptions(std::ios::failbit);, but instead check for is.fail().

如果解析以任何方式失败,将抛出异常。如果您不想抛出异常,请不要is.exceptions(std::ios::failbit);,而是检查is.fail().

回答by Yippie-Ki-Yay

You could utilize the boost::date_timeand write a small manual parser (probably regexp-based)for your strings.

您可以使用boost::date_time并为您的字符串编写一个小型手动解析器(可能基于正则表达式)

回答by casablanca

Problem here is that mktime uses local time not UTC time.

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

How about just computing the time difference between UTC and local time, then adding it to the value returned by mktime?

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

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

回答by Dirk Eddelbuettel

What's wrong with strptime()?

怎么了strptime()

And on Linux, you even get the 'seconds east of UTC' field relieving you from any need to parse:

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

which for me yields

这对我来说产生

/tmp$ g++ -o my my.cpp 
/tmp$ ./my
2010-11-04T23:23:01Z -> 04 Nov 2010 23:23:01
Seconds east of UTC 140085769590024

回答by caf

X/Open provides a global timezonevariable which indicates the number of seconds that local time is behind UTC. You can use this to adjust the output of mktime():

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