【问题标题】:A class for date and time representation in C++11C++11 中的日期和时间表示类
【发布时间】:2022-01-12 16:17:21
【问题描述】:

我需要一个能够以 UTC 格式存储日期和时间信息的类(不需要时区/语言环境),我还需要对这些日期/时间(加法、减法)和比较执行基本算术运算。理想情况下,如果可能的话,我想使用 C++11 标准库。

std::chrono 似乎是我的理想人选。我的DateTime 类可以像chrono::time_point 的别名一样简单,具有所需的精度。例如

using UTCTime = std::chrono::time_point<SomeClock>;

定义自定义时钟 (SomeClock) 允许我的类是类型安全的,而chrono::time_point 类提供计算要求。

难题中缺少的部分是转换日期和时间信息的函数。 chrono::system_clock提供了to_time_tfrom_time_t这两个方法,受限于秒的精度,对我来说不够好。

有人有解决办法吗?

【问题讨论】:

  • 我不知道这是否有帮助:github.com/HowardHinnant/date
  • 它“基本上”是 C++20 的 <chrono> 中的内容,但可作为 C++11 项目的单个头库。
  • 好老的tm怎么样?如果您需要亚秒级精度,您可以在日期旁边存储一些(毫秒?)秒,然后手动操作。
  • @HolyBlackCat,用于存储数据可以正常工作。但是我会失去 chrono::time_point 带来的算术/操作/类型安全
  • 类型安全是的,但是可以通过转换为 time_t 并返回来修复任何超出范围的字段来完成算术运算。

标签: c++ datetime c++11


【解决方案1】:

仅供参考,这是 C++20 中的样子...

using UTCTime = std::chrono::time_point<UTCClock, std::chrono::microseconds>;

变成:

using UTCTime = std::chrono::sys_time<std::chrono::microseconds>;

这个:

//
// use UTCClock::fromDate to generate a UTCTime 
//
UTCTime t1 = UTCClock::fromDate(1901, 1,  1, 0, 0, 0, 0);
UTCTime t2 = UTCClock::fromDate(1901, 1,  1, 1, 0, 0, 0);

变成:

UTCTime t1 = sys_days{1901y/1/1};
UTCTime t2 = sys_days{1901y/1/1} + 1h;

这个:

//
// Then we can make use of std::chrono algebra like
// subtracting time_point 
//
microseconds timeDiff = t2 -t1;
std::cout << "t2-t1 difference in microseconds " << timeDiff.count() << std::endl;
std::cout << "t2-t1 difference in hours " << duration_cast<hours>(timeDiff).count() << std::endl;

变成:

//
// Then we can make use of std::chrono algebra like
// subtracting time_point 
//
microseconds timeDiff = t2 - t1; 
std::cout << "t2-t1 difference in microseconds " << timeDiff << '\n';
std::cout << "t2-t1 difference in hours " << duration_cast<hours>(timeDiff) << '\n';

这个:

//
// ...or adding/subtracting a duration to a time_point
//    
UTCTime t3 = t1 - minutes{3};
std::cout << "t3-t1 difference in minutes " << duration_cast<minutes>(t3-t1).count() << std::endl;

t3 = t1 + milliseconds{123};
std::cout << "t3-t1 difference in milliseconds "
          << duration_cast<milliseconds>(t3-t1).count() << std::endl;

变成:

//
// ...or adding/subtracting a duration to a time_point
//    
UTCTime t3 = t1 - 3min;
std::cout << "t3-t1 difference in minutes " << duration_cast<minutes>(t3-t1) << '\n';

t3 = t1 + 123ms;
std::cout << "t3-t1 difference in milliseconds "
          << duration_cast<milliseconds>(t3-t1) << '\n';

这个:

//
// ...we can also compare time_points
//    
if ( t3 > t1 ) std::cout <<  "t3 is greater than t1" << std::endl;

保持不变。


这个:

//
// We can get a date/time back from a time_point with UTCClock::toDate
//
int year, month, day, hour, min, sec, usec;
UTCClock::toDate(t3, year, month, day, hour, min, sec, usec);

变成:

//
// We can get a date/time back from a time_point with UTCClock::toDate
//
auto td = floor<days>(t3);
year_month_day ymd = td;
hh_mm_ss hms{t3-td};

ymd 具有年、月和日的 getter,hms 具有小时、分钟、秒和微秒的 getter。


输出变化:

t2-t1 difference in microseconds 3600000000
t2-t1 difference in hours 1
t3-t1 difference in minutes -3
t3-t1 difference in milliseconds 123
t3 is greater than t1

到:

t2-t1 difference in microseconds 3600000000µs
t2-t1 difference in hours 1h
t3-t1 difference in minutes -3min
t3-t1 difference in milliseconds 123ms
t3 is greater than t1

【讨论】:

  • 谢谢,这真的很有用。我希望我可以立即使用 c++20,但我目前需要与旧操作系统兼容
【解决方案2】:

假设您以毫秒精度存储日期和时间信息,那么您总是可以这样做:

time_point<system_clock, milliseconds> epoch_milli{};
auto one_million_millisecond_from_epoch = epoch_milli + milliseconds(1'000'000);

【讨论】:

    【解决方案3】:

    由于我不能使用 C++20,我最终得到了这个基于 C++11 的最小 std::chrono 实现:

    utctime.h

    #include <chrono>
    #include <string>
    
    struct UTCClock
    {
      typedef std::chrono::microseconds duration;
      typedef duration::rep rep;
      typedef duration::period period;
      typedef std::chrono::time_point<UTCClock, duration> time_point;
      static const bool is_steady = true;
    
      //      
      // every time_point will be generated from here
      //
      static time_point fromDate(int year= 0, int month= 0, int day= 0, 
                                 int hour= 0, int min  = 0, int sec= 0,
                                 int usec= 0);
      //
      // convert time_point to a date/time representation
      //
      static void toDate(const time_point &tp,
                         int &year, int &month, int &day,
                         int &hour, int &min, int &sec,
                         int &usec);
    
      // NOT Supported, we don't need current time. We only
      // want to represent UTC DateTime
      // static time_point now(); 
    };
    
    using UTCTime = std::chrono::time_point<UTCClock, std::chrono::microseconds>;
    

    utctime.cpp

    #include "utctime.h"
    #include <ctime>
    
    namespace chrono = std::chrono;
    using chrono::duration_cast;
    using chrono::time_point_cast; 
    
    namespace {
    
    std::time_t to_time_t(const UTCClock::time_point &tp) noexcept
    {
      return std::time_t(
          duration_cast<chrono::seconds>(tp.time_since_epoch()).count());
    }
    
    UTCClock::time_point from_time_t(std::time_t tt) noexcept
    {
      return time_point_cast<UTCClock::duration>(
         chrono::time_point<UTCClock,chrono::seconds>(chrono::seconds(tt)));
    }
    
    } // namespace
    
    
    UTCClock::time_point UTCClock::fromDate(
        int year, int month, int day, int hour, int min, int sec, int usec)
    {
      std::tm tm     = {0};
      tm.tm_year     = year - 1900;
      tm.tm_mon      = month - 1;
      tm.tm_mday     = day;
      tm.tm_hour     = hour;
      tm.tm_min      = min;
      tm.tm_sec      = sec;
      tm.tm_isdst    = -1;
      std::time_t tt = timegm(&tm);
      return from_time_t(tt) + chrono::microseconds(usec);
    }
    
    void UTCClock::toDate(const UTCClock::time_point &tp,
                          int &year,
                          int &month,
                          int &day,
                          int &hour,
                          int &min,
                          int &sec,
                          int &usec)
    {
      std::time_t tt = to_time_t(tp);
      std::tm tm;
      gmtime_r(&tt, &tm);
      year  = tm.tm_year + 1900;
      month = tm.tm_mon + 1;
      day   = tm.tm_mday;
      hour  = tm.tm_hour;
      min   = tm.tm_min;
      chrono::microseconds leftover =
          tp - from_time_t(tt) + chrono::seconds(tm.tm_sec);
      sec = duration_cast<chrono::seconds>(leftover).count();
      usec = (leftover-chrono::seconds(sec)).count();
    }
    

    可以这样使用:

    #include "utctime.h"
    #include <iostream>
    
    using namespace std::chrono;
    
    int main(int argc, char* argv[])
    {
      //
      // use UTCClock::fromDate to generate a UTCTime 
      //
      UTCTime t1 = UTCClock::fromDate(1901, 1,  1, 0, 0, 0, 0);
      UTCTime t2 = UTCClock::fromDate(1901, 1,  1, 1, 0, 0, 0);
    
      //
      // Then we can make use of std::chrono algebra like
      // subtracting time_point 
      //
      microseconds timeDiff = t2 -t1; 
    
      std::cout << "t2-t1 difference in microseconds " << 
        timeDiff.count() << std::endl;
      std::cout << "t2-t1 difference in hours " <<
        duration_cast<hours>(timeDiff).count() << std::endl;
    
      //
      // ...or adding/subtracting a duration to a time_point
      //    
      UTCTime t3 = t1 - minutes{3};
      std::cout << "t3-t1 difference in minutes " <<
        duration_cast<minutes>(t3-t1).count() << std::endl;
    
      t3 = t1 + milliseconds{123};
      std::cout << "t3-t1 difference in milliseconds " <<
        duration_cast<milliseconds>(t3-t1).count() << std::endl;
    
      //
      // ...we can also compare time_points
      //    
      if ( t3 > t1 ) std::cout <<  "t3 is greater than t1" << std::endl;
    
      //
      // We can get a date/time back from a time_point with UTCClock::toDate
      //
      int year, month, day, hour, min, sec, usec;
      UTCClock::toDate(t3, year, month, day, hour, min, sec, usec);
    
      //
      // std::chrono is also type safe and doesn't allow to
      // mix dates generated by different clocks
      //
      system_clock::time_point systp = system_clock::now();
    
      // NO: doesn't compile
      //  auto tx = t1 - systp;
    
      // NO: doesn't compile
      //  if (t1 < systp);
    
      // NO: doesn't compile
      // UTCClock::toDate(systp, year, month, day, hour, min, sec, usec);
    
      return 0;
    }
    

    输出:

    t2-t1 difference in microseconds 3600000000
    t2-t1 difference in hours 1
    t3-t1 difference in minutes -3
    t3-t1 difference in milliseconds 123
    t3 is greater than t1
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-04
      • 2013-11-02
      • 1970-01-01
      • 2012-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多