【问题标题】:C++: Converting a date in form 20160120 to days since epochC++:将 20160120 形式的日期转换为纪元以来的天数
【发布时间】:2016-11-05 13:26:09
【问题描述】:

假设我们有一个表示日期的整数,格式为 20160120。我想将其转换为自纪元 (1970-01-01) 以来的天数,以便它对应于 POSIX 样式的日期。因此,例如 int 20160718 将变为 int 17000。

我的尝试如下,我首先将 int 转换为字符串,然后转换为自纪元以来的秒数,然后转换为自纪元以来的天数。这样做的一个问题是我必须将日期移动 3 小时才能获得正确的日期(请参阅std::to_string(d*100 + 3))行。我怀疑这与时区有关?我在UTC - 1。我不知道如何处理。

所以我想知道是否有一种不太复杂的方法,如果没有,我该如何解决时区问题。

#include <iostream>
#include <chrono>
#include <time.h>
#include <iomanip>
#include <sstream>
#include <string>

typedef std::chrono::duration<int, std::ratio<60 * 60 * 24>> days_type;
typedef std::chrono::system_clock sysclock;

int convert(const unsigned d){
    std::tm t = {};
    std::istringstream ss(std::to_string(d*100 + 3));   
    ss >> std::get_time(&t, "%Y%m%d%H");

    time_t t_ =  mktime(&t);

    /*
     Now convert seconds since epoch to days since epoch via chrono...
     */

    sysclock::time_point tp = sysclock::from_time_t(t_);

    std::chrono::time_point<sysclock, days_type> tp_day = 
    std::chrono::time_point_cast<days_type>(tp);

    return tp_day.time_since_epoch().count();
}


int main(){
    int d = 20160718;
    std::cout << d << ": " << convert(d) << std::endl; //17000
    return 0;
}

【问题讨论】:

    标签: c++ date chrono


    【解决方案1】:

    使用date library

    #include "date.h"
    #include <iostream>
    
    int convert(unsigned d) {
      date::year_month_day ymd{date::year(d / 100 / 100),
                               date::month(d / 100 % 100), date::day(d % 100)};
      return date::sys_days{ymd}.time_since_epoch().count();
    }
    
    int main() {
      int d = 20160718;
      std::cout << d << ": " << convert(d) << '\n'; // 17000
    }
    

    See it run

    当然,convert() API 会通过更强的类型来改进,例如直接返回一个date::sys_days 来表示时间点。

    【讨论】:

    【解决方案2】:

    time_t 可能不是您系统上 POSIX 纪元开始时的 0

    这是一个显示不同结果的程序:

    #include "date.h" // uses this library: https://github.com/HowardHinnant/date
    #include <iostream>
    #include <chrono>
    #include <ctime>
    #include <iomanip>
    #include <string>
    
    time_t convert_local(int d)
    {
        std::tm t{};
        auto ts{std::to_string(d)};
        ts.insert(8, " ");
        ts.insert(6, " ");
        ts.insert(4, " "); // this is a workaround for a VS2015 bug: http://stackoverflow.com/q/21172767/1460794
        std::istringstream ss(ts);
        ss >> std::get_time(&t, "%Y %m %d");
        if(!ss) std::cout << "parse fail.\n";
    
        time_t t_{mktime(&t)};
        return t_;
    }
    
    time_t convert_UTC(int d)
    {
        std::tm base_t{};
        base_t.tm_mday = 1;
        base_t.tm_mon = 0;
        base_t.tm_year = 70;
        base_t.tm_wday = 4;
        auto base_tt{std::mktime(&base_t)};
    
        std::tm t{};
        auto ts{std::to_string(d)};
        ts.insert(8, " ");
        ts.insert(6, " ");
        ts.insert(4, " "); // this is a workaround for a VS2015 bug: http://stackoverflow.com/q/21172767/1460794
        std::istringstream ss(ts);
        ss >> std::get_time(&t, "%Y %m %d");
        if(!ss) std::cout << "parse fail.\n";
    
        time_t t_{mktime(&t)};
        return t_ - base_tt;
    }
    
    typedef std::chrono::duration<int, std::ratio<60 * 60 * 24>> days_type;
    typedef std::chrono::system_clock sysclock;
    
    int days_since_epoch(time_t t)
    {
        sysclock::time_point tp = sysclock::from_time_t(t);
        std::chrono::time_point<sysclock, days_type> tp_day =
            std::chrono::time_point_cast<days_type>(tp);
        return tp_day.time_since_epoch().count();
    }
    
    int main()
    {
        std::tm t{};
    
        t.tm_mday = 1;
        t.tm_mon = 0;
        t.tm_year = 70;
        t.tm_wday = 4;
    
        auto tt{std::mktime(&t)};
        auto tp{std::chrono::system_clock::from_time_t(tt)};
    
        time_t t0{0};
        auto tp0{std::chrono::system_clock::from_time_t(t0)};
    
        using namespace date;
        std::cout << tp << " for Jan 1 1970 00:00 UTC in my timezone (-6)\n";
        std::cout << std::chrono::system_clock::now() << " is the time now in my timezone (-6)\n";
        std::cout << tp0 << " is what we get from time_t{0} in my timezone (-6)\n";
    
        int d = 20160718;
        std::cout << "\n\n";
        std::cout << d << " converted to time_t is " << convert_local(d) << " but it considers local timezone on my system.\n";
        std::cout << "Now, using this time_t, converted to days since epoch: " << days_since_epoch(convert_local(d));
    
        std::cout << "\n\n";
        std::cout << d << " converted to time_t, adjusted for local timezone is " << convert_UTC(d) << ".\n";
        std::cout << "Now, using this time_t, converted to days since epoch: " << days_since_epoch(convert_UTC(d));
    
        return 0;
    }
    

    在我的系统上它产生:

    1970-01-01 06:00:00.0000000 for Jan 1 1970 00:00 UTC in my timezone (-6)
    2016-11-05 14:42:19.1299886 is the time now in my timezone (-6)
    1970-01-01 00:00:00.0000000 is what we get from time_t{0} in my timezone (-6)
    
    
    20160718 converted to time_t is 1468821600 but it considers local timezone on my system.
    Now, using this time_t, converted to days since epoch: 17000
    
    20160718 converted to time_t, adjusted for local timezone is 1468800000.
    Now, using this time_t, converted to days since epoch: 17000
    

    在另一台服务器 (live demo) 上生成:

    1970-01-01 00:00:00.000000000 for Jan 1 1970 00:00 UTC in my timezone
    2016-11-05 14:37:15.661541264 is the time now in my timezone
    1970-01-01 00:00:00.000000000 is what we get from time_t{0} in my timezone
    
    
    20160718 converted to time_t is 1468800000 but it considers local timezone on my system.
    Now, using this time_t, converted to days since epoch: 17000
    
    20160718 converted to time_t, adjusted for local timezone is 1468800000.
    Now, using this time_t, converted to days since epoch: 17000
    

    最后,最好为每个时间点明确地获取您想要使用的两个时间点(通过指定日、月和年)。

    这样您就可以使用system_clock,这并不重要,因为我们只希望以天为单位计算持续时间:

    #include <iostream>
    #include <chrono>
    #include <ctime>
    #include <iomanip>
    #include <string>
    #include <sstream>
    
    auto get_epoch_tp()
    {
        std::tm base_t{};
        base_t.tm_mday = 1;
        base_t.tm_mon = 0;
        base_t.tm_year = 70;
        base_t.tm_wday = 4;
        auto base_tt{std::mktime(&base_t)};
        auto tp{std::chrono::system_clock::from_time_t(base_tt)};
        return tp;
    }
    
    int get_days_since_epoch(int d)
    {
        std::tm t{};
        auto ts{std::to_string(d)};
        ts.insert(8, " ");
        ts.insert(6, " ");
        ts.insert(4, " "); // this is a workaround for a VS2015 bug: http://stackoverflow.com/q/21172767/1460794
        std::istringstream ss(ts);
        ss >> std::get_time(&t, "%Y %m %d");
        if(!ss) std::cout << "parse fail.\n";
        time_t tt{mktime(&t)};
    
        auto tp{std::chrono::system_clock::from_time_t(tt)};
    
        using days_type = std::chrono::duration<int, std::ratio<60 * 60 * 24>>;
        auto duration{tp - get_epoch_tp()};
        auto days{std::chrono::duration_cast<days_type>(duration)};
        return days.count();
    }
    
    int main()
    {
        int d = 20160718;
        std::cout << d << ": " << get_days_since_epoch(d) << std::endl; //17000
    }
    

    demo

    【讨论】:

      【解决方案3】:

      您可以在转换为时间后进行简单的数学运算。

      #include <iostream>
      #include <time.h>
      #include <iomanip>
      #include <sstream>
      
      int convert(const unsigned d){
          std::tm t = {};
          std::istringstream ss(std::to_string(d));  
          ss >> std::get_time(&t, "%Y%m%d");
      
          time_t t_ = mktime(&t);
          return (int)t_/(60*60*24);
      }
      
      
      int main(){
          int d = 20160718;
          std::cout << d << ": " << convert(d) << std::endl; //17000
          return 0;
      }
      

      【讨论】:

      • 好吧,首先给我 16999,而不是 17000。但总的来说 - 时区、闰年、闰秒等等呢?这就是为什么我没有简单地除以一天中的秒数的原因。
      • 我不认为这是个坏主意。这种方法是具有双射功能的简单方法。天应该被看作是一个时间单位,正好需要 86400 秒。
      • @LukaRahne 如果第二个增量和日期是双射映射会很好,但在现实世界中并非如此......
      猜你喜欢
      • 2017-02-20
      • 2010-09-13
      • 2012-08-25
      • 2014-03-13
      • 2011-08-31
      • 1970-01-01
      • 1970-01-01
      • 2015-01-23
      相关资源
      最近更新 更多