C/C++时区正确的时间转换(自纪元以来的秒数)

jma*_*jma 4 c++ time

我想知道自纪元以来的几秒钟.值得注意的是,我不喜欢转换机器的位置,时区字符串应该足够了.

我有这个测试程序,pt.cc:

#include <assert.h>
#include <errno.h>
#include <iostream>
#include <stdio.h>
#include <string>
#include <string.h>
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE
#endif
#include <time.h>

using namespace std;  // To be brief, don't do this in real life.

int main(int argc, char* argv[]) {
    (void)argc; (void)argv;   // Skip compile warning.

    // I expect both of these to transform to 1440671500.
    cout << "1440671500 expected" << endl;
    const char utc_example[] = "2015-08-27T11:31:40+0100";
    struct tm tm;
    memset(&tm, 0, sizeof(struct tm));
    char* end = strptime(utc_example, "%Y-%m-%dT%H:%M:%S%z", &tm);
    assert(end);
    assert(*end == '\0');
    time_t seconds_since_epoch = mktime(&tm);
    cout << "utc example: " << seconds_since_epoch << "  or maybe  "
         << seconds_since_epoch - tm.tm_gmtoff + (tm.tm_isdst ? 3600 : 0) << endl;

    const char tz_example[] = "2015-08-27T10:31:40Z";
    memset(&tm, 0, sizeof(struct tm));
    end = strptime(tz_example, "%Y-%m-%dT%H:%M:%S%nZ", &tm);
    assert(end);
    assert(*end == '\0');
    seconds_since_epoch = mktime(&tm);
    cout << " tz example: " << seconds_since_epoch << "  or maybe  "
         << seconds_since_epoch - tm.tm_gmtoff + (tm.tm_isdst ? 3600 : 0) << endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是输出:

jeff@birdsong:tmp $ clang++ -ggdb3 -Wall -Wextra -std=c++14 pt.cc -o pt
jeff@birdsong:tmp $ ./pt
1440671500 expected
utc example: 1440671500  or maybe  1440667900
 tz example: 1440667900  or maybe  1440664300
jeff@birdsong:tmp $ TZ=America/New_York ./pt
1440671500 expected
utc example: 1440693100  or maybe  1440711100
 tz example: 1440689500  or maybe  1440707500
jeff@birdsong:tmp $ TZ=Europe/London ./pt
1440671500 expected
utc example: 1440675100  or maybe  1440675100
 tz example: 1440671500  or maybe  1440671500
jeff@birdsong:tmp $ 
Run Code Online (Sandbox Code Playgroud)

请注意返回值的mktime()变化取决于环境时区.手册页条目mktime()建议将故障时间解释为当地时间.所以我尝试减去GMT偏移并补偿时区,以防它忽略那些值("或者"值).

有关如何正确执行此操作的任何提示?(重要的是,我只需要在linux上工作.)

How*_*ant 10

这个答案使用这个日期/时间库:

http://howardhinnant.github.io/date/date.html

这里采用的方法是完全绕过C日期/时间API.就个人而言,我发现C方法有点混乱,麻烦,有点危险.

话虽这么说,我的日期/时间库中的解析和格式化设施是不存在的.我预计这些设施可能会成为一个单独的图书馆,将来会在我的图书馆之上分层.

与此同时,为这个特定问题推出自己的解析并不困难.方法如下:

#include "chrono_io.h"
#include "date.h"
#include <iostream>
#include <string>
#include <sstream>

using second_point = std::chrono::time_point<std::chrono::system_clock,
                                             std::chrono::seconds>;

std::chrono::minutes
parse_offset(std::istream& in)
{
    using namespace std::chrono;
    char c;
    in >> c;
    minutes result = 10*hours{c - '0'};
    in >> c;
    result += hours{c - '0'};
    in >> c;
    result += 10*minutes{c - '0'};
    in >> c;
    result += minutes{c - '0'};
    return result;
}

second_point
parse(const std::string& str)
{
    std::istringstream in(str);
    in.exceptions(std::ios::failbit | std::ios::badbit);
    int yi, mi, di;
    char dash;
    // check dash if you're picky
    in >> yi >> dash >> mi >> dash >> di;
    using namespace date;
    auto ymd = year{yi}/mi/di;
    // check ymd.ok() if you're picky
    char T;
    in >> T;
    // check T if you're picky
    int hi, si;
    char colon;
    in >> hi >> colon >> mi >> colon >> si;
    // check colon if you're picky
    using namespace std::chrono;
    auto h = hours{hi};
    auto m = minutes{mi};
    auto s = seconds{si};
    second_point result = sys_days{ymd} + h + m + s;
    char f;
    in >> f;
    if (f == '+')
        result -= parse_offset(in);
    else if (f == '-')
        result += parse_offset(in);
    else
        ;// check f == 'Z' if you're picky
    return result;
}

int
main()
{
    using namespace date;
    std::cout << parse("2015-08-27T11:31:40+0100").time_since_epoch() << '\n';
    std::cout << parse("2015-08-27T10:31:40Z").time_since_epoch() << '\n';
}
Run Code Online (Sandbox Code Playgroud)

要完全前期,该解决方案使主要用途的std::istringstream,std::chrono以及实际上只是它的非常小的一部分是我最新的图书馆.

我做了几个设计选择,您可以选择不选择(解析时有很多选项).例如,如果存在任何解析错误,我选择抛出异常,并且我选择不挑选检查分隔符等(-并且:主要是出于简洁的原因).

代码相对不言自明.正如您在问题中所述,本地时区不是(也不应该是)解决方案的一部分.计时库用于在小时,分钟和秒之间管理算术.我的日期库用于处理年/月/日转换chrono::time_point为精度为days.

处理完所有这些算术后,您可以专注于解析整数和字符.可以直接向此示例添加更多检查,并为错误执行任何操作.

此示例输出:

1440671500s
1440671500s
Run Code Online (Sandbox Code Playgroud)

更新

我已经添加了解析功能,现在可以更简单地编写"date.h"上面的parse函数:

date::sys_seconds
parse(const std::string& str)
{
    std::istringstream in(str);
    date::sys_seconds tp;
    in >> date::parse("%FT%TZ", tp);
    if (in.fail())
    {
        in.clear();
        in.str(str);
        in >> date::parse("%FT%T%z", tp);
    }
    return tp;
}

int
main()
{
    using namespace date;
    std::cout << parse("2015-08-27T11:31:40+0100").time_since_epoch() << '\n';
    std::cout << parse("2015-08-27T10:31:40Z").time_since_epoch() << '\n';
}
Run Code Online (Sandbox Code Playgroud)

并返回相同的结果.


Gre*_*ler 3

这是一个使用 Google 的https://github.com/google/cctz执行您想要的操作的答案

#include <chrono>
#include <iostream>
#include <string>

#include "src/cctz.h"

using namespace std;

int main(int argc, char* argv[]) {
  const char kFmt[] = "%Y-%m-%dT%H:%M:%S%Ez";

  // I expect both of these to transform to 1440671500.
  const char utc_example[] = "2015-08-27T11:31:40+0100";
  const char tz_example[] = "2015-08-27T10:31:40Z";
  cout << "1440671500 expected" << endl;

  // Required by cctz::Parse(). Only used if the formatted
  // time does not include offset info.
  const auto utc = cctz::UTCTimeZone();

  std::chrono::system_clock::time_point tp;
  if (!Parse(kFmt, utc_example, utc, &tp)) return -1;
  cout << "utc example: " << std::chrono::system_clock::to_time_t(tp) << "\n";

  if (!Parse(kFmt, tz_example, utc, &tp)) return -1;
  cout << " tz example: " << std::chrono::system_clock::to_time_t(tp) << "\n";

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出是:

1440671500 expected
utc example: 1440671500
 tz example: 1440671500
Run Code Online (Sandbox Code Playgroud)

请注意,涉及添加/减去 time_t 等偏移量的其他答案正在使用一种称为“纪元移位”的技术,但它实际上不起作用。我在 CppCon 的 12:30 演讲中解释了原因: https: //youtu.be/2rnIHsqABfM?t=12m30s