如何转换std :: chrono :: time_point到std :: tm而不使用time_t? [英] How to convert std::chrono::time_point to std::tm without using time_t?

查看:1274
本文介绍了如何转换std :: chrono :: time_point到std :: tm而不使用time_t?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想打印或提取年/月/日的值。



我不想使用 time_t 因为2038年的问题,但所有的例子,我发现在互联网上使用它将 time_point 转换为 tm



有一个简单的方法可以从time_point转换为tm(最好是不用boost )?






timesub libc 的实现将是我最后的手段:
http://www.opensource.apple.com/source/Libc/ Libc-262 / stdtime / localtime.c






编辑:建议的链接和做一些更多的研究,我得出以下结论。


  1. 使用time_t其中64位长是确定)。

值得注意的是Boost .Date_Time可以是只有标题的库。资料来源:
http ://www.boost.org/doc/libs/1_53_0/more/getting_started/unix-variants.html#header-only-libraries

解决方案

使用更好的算法更新答案,链接到算法的详细说明,并完成转换为 std :: tm







我想打印或提取年/月/日值。
有一个简单的方法来从time_point转换为tm(最好是
,不用boost)?


要注意的是, std :: chrono :: time_point 不仅在 duration 上模板化, 。时钟意味着一个时代。



例如,在我的系统上, std :: chrono :: high_resolution_clock std :: chrono :: steady_clock 有一个时代:每当计算机启动。如果您不知道计算机启动的时间,则无法将 time_point 转换为任何日历系统。



话虽如此,你可能只是说 std :: chrono :: system_clock :: time_point ,因为 time_point ,并且只有此 time_point 才需要与公民(gregorian)日历具有确定性关系。



事实证明, std :: chrono :: system_clock 的每个实现我知道是使用 unix时间。这有一个新年1970年忽略闰秒的时代。



这不是标准保证。但是,如果您想使用以下公式找到以下公式,您可以利用此事实:


chrono-兼容低级日期算法


首先,警告,我使用最新的C ++ 1y草稿,其中包括大量新的 constexpr 工具。如果你需要为你的编译器回退一些 constexpr 属性,就这样做。



找到上面的链接,你可以将 std :: chrono :: time_point< std :: chrono :: system_clock,Duration> 转换为 std :: tm ,而不使用 time_t 具有以下函数:

  template< class Duration> 
std :: tm
make_utc_tm(std :: chrono :: time_point< std :: chrono :: system_clock,Duration> tp)
{
using namespace std;
using namespace std :: chrono;
typedef duration< int,ratio_multiply< hours :: period,ratio< 24>>天;
// t是自1970-01-01之后的持续时间
持续时间t = tp.time_since_epoch();
// d是自1970-01-01
天之后的天数d = round_down< days>(t);
// t现在是从天的午夜起的持续时间d
t - = d;
//将d下降为年/月/日
int年;
unsigned month;
unsigned day;
std :: tie(year,month,day)= civil_from_days(d.count());
//用日历信息填充tm
std :: tm tm = {0};
tm.tm_year = year - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = day;
tm.tm_wday = weekday_from_days(d.count());
tm.tm_yday = d.count() - days_from_civil(year,1,1);
//填充时间
tm.tm_hour = duration_cast< hours>(t).count();
t - = hours(tm.tm_hour);
tm.tm_min = duration_cast< minutes>(t).count();
t - = minutes(tm.tm_min);
tm.tm_sec = duration_cast< seconds>(t).count();
return tm;
}

另请注意, std :: chrono ::所有现有实现上的system_clock :: time_point 是UTC(忽略闰秒)时区的持续时间。如果要使用另一个时区转换 time_point ,则需要将时区的持续时间偏移加到/减去 std :: chrono: :system_clock :: time_point ,然后将其转换为的精度。如果您还想考虑闰秒,请使用天。 wikipedia.org/wiki/Leap_seconds>此表格,以及知道unix时间与UTC现在 相符。



此函数可通过以下方式验证:

  #include< iostream> 
#include< iomanip>

void
print_tm(const std :: tm& tm)
{
using namespace std;
cout<< tm.tm_year + 1900;
char fill = cout.fill();
cout<< setfill('0');
cout<< ' - '<< setw(2)<< tm.tm_mon + 1;
cout<< ' - '<< setw(2)<< tm.tm_mday;
cout<< '';
switch(tm.tm_wday)
{
case 0:
cout< 太阳;
break;
case 1:
cout<< Mon;
break;
case 2:
cout<< Tue;
break;
case 3:
cout<< 星期三;
break;
case 4:
cout<< Thu;
break;
case 5:
cout<< Fri;
break;
case 6:
cout< Sat;
break;
}
cout<< '';
cout<< ''<< setw(2)<< tm.tm_hour;
cout<< ':'<< setw(2)<< tm.tm_min;
cout<< ':'<< setw(2)<< tm.tm_sec<< 世界标准时间。;
cout<< setfill(fill);
cout<< 这是< tm.tm_yday< 天1月1日以来
}

int
main()
{
print_tm(make_utc_tm(std :: chrono :: system_clock :: now()));
}

我目前打印出来的内容:


2013-09-15 Sun 18:16:50 UTC。这是自1月1日以来的257天


如果 chrono-Compatible低级日期算法离线或移动,以下是 make_utc_tm 中使用的算法。在上述链路上有这些算法的深入解释。

  //返回自1970-01年以来的天数-01。负值表示
// 1970-01-01之前的天数。
//前提条件:ymd表示民事(公历)日历中的日期
// m在[1,12]中
// d在[1,last_day_of_month )]
//在
中是近似// [numeric_limits< Int> :: min()/ 366,numeric_limits< Int> :: max()/ 366]
/ /有效性的准确范围是:
// [civil_from_days(numeric_limits< Int> :: min()),
// civil_from_days(numeric_limits< Int> :: max() - 719468)]
template< class Int>
constexpr
Int
days_from_civil(Int y,unsigned m,unsigned d)noexcept
{
static_assert(std :: numeric_limits< unsigned> :: digits> = 18,
此算法尚未移植到16位无符号整数);
static_assert(std :: numeric_limits< Int> :: digits> = 20,
此算法未移植到16位有符号整数);
y - = m <= 2;
const Int era =(y> =0≤y:y-399)/ 400;
const unsigned yoe = static_cast< unsigned>(y-era * 400); // [0,399]
const unsigned doy =(153 *(m +(m>2≤-3:9))+ 2)/ 5 + d-1; // [0,365]
const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0,146096]
return era * 146097 + static_cast< Int>(doe) - 719468;
}

//返回公历中的年/月/日三元组
//前提条件:z是自1970-01-01以来的天数,范围为:
// [numeric_limits< Int> :: min(),numeric_limits< Int> :: max() - 719468]。
template< class Int>
constexpr
std :: tuple< Int,unsigned,unsigned>
civil_from_days(Int z)noexcept
{
static_assert(std :: numeric_limits< unsigned> :: digits> = 18,
)此算法尚未移植到16位无符号整数);
static_assert(std :: numeric_limits< Int> :: digits> = 20,
此算法未移植到16位有符号整数);
z + = 719468;
const Int era =(z> = 0?z:z - 146096)/ 146097;
const unsigned doe = static_cast< unsigned>(z - era * 146097); // [0,146096]
const unsigned yoe =(doe - doe / 1460 + doe / 36524 - doe / 146096)/ 365; // [0,399]
const Int y = static_cast< Int>(yoe)+ era * 400;
const unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0,365]
const unsigned mp =(5 * doy + 2)/ 153; // [0,11]
const unsigned d = doy - (153 * mp + 2)/ 5 + 1; // [1,31]
const unsigned m = mp +(mp <10?3:-9); // [1,12]
return std :: tuple< Int,unsigned,unsigned>(y +(m <= 2),m,d)
}

template< class Int>
constexpr
unsigned
weekday_from_days(Int z)noexcept
{
return static_cast< unsigned>(z> = -4?(z + 4) (z + 5)%7 + 6);
}

template< class To,class Rep,class Period>

round_down(const std :: chrono :: duration< Rep,Period>& d)
{
To t = std :: chrono :: duration_cast& (d)。
if(t> d)
--t;
return t;
}

更新

最近,我将上述算法包装到免费的日期/时间库中在此处记录和提供< a>。这个库使得从 std :: system_clock :: time_point ,甚至小时:分钟:秒:分秒秒提取年/月/日非常容易。所有这些都不经过 time_t



这里是一个简单的程序, UTC时区中的当前日期和时间,以及 system_clock :: time_point 提供(在此情况下为微秒)的精度:

  #includedate.h
#include< iostream>


int
main()
{
使用命名空间date;
using namespace std;
using namespace std :: chrono;
auto const now = system_clock :: now();
auto const dp = time_point_cast< days>(now);
auto const date = year_month_day(dp);
auto const time = make_time(now-dp);
cout<<日期<< ''<<时间< UTC\\\
;
}

这只是输出给我:

  2015-05-19 15:03:47.754002 UTC 


$ b b

此库有效地将 std :: chrono :: system_clock :: time_point 变为易于使用的日期时间类型。


I would like to print or extract year/month/day values.

I don't want to use time_t because of the year 2038 problem, but all examples I found on the Internet use it to convert time_point to tm.

Is there a simple way to convert from time_point to tm (preferably without boost)?


An implementation like timesub from libc would be my last resort: http://www.opensource.apple.com/source/Libc/Libc-262/stdtime/localtime.c


Edit: After reading the suggested links and doing some more research, I came to the following conclusion.

  1. Using time_t where it is 64bit long is ok (for most purposes).
  2. Using Boost.Date_Time for portable code.

It is noteworthy that Boost.Date_Time can be a header-only library. Source: http://www.boost.org/doc/libs/1_53_0/more/getting_started/unix-variants.html#header-only-libraries

解决方案

Answer updated with better algorithms, link to detailed description of the algorithms, and complete conversion to std::tm.


I would like to print or extract year/month/day values. Is there a simple way to convert from time_point to tm (preferably without boost)?

The first thing to note is that std::chrono::time_point is templated not only on duration, but also on the clock. The clock implies an epoch. And different clocks can have different epochs.

For example, on my system, std::chrono::high_resolution_clock and std::chrono::steady_clock have an epoch of: whenever the computer booted up. If you don't know what time the computer booted up, there is no way to convert that time_point to any calendar system.

That being said, you were probably talking just about std::chrono::system_clock::time_point, as this time_point, and only this time_point, is required to have a deterministic relationship with the civil (gregorian) calendar.

As it turns out, every implementation of std::chrono::system_clock I'm aware of is using unix time. This has an epoch of New Years 1970 neglecting leap seconds.

This isn't guaranteed by the standard. However you can take advantage of this fact if you want to with the following formulas found at:

chrono-Compatible Low-Level Date Algorithms

First off, warning, I'm using the latest C++1y draft, which includes great new constexpr tools. If you need to back off some of the constexpr attributes for your compiler, just do so.

Given the algorithms found at the above link, you can can convert a std::chrono::time_point<std::chrono::system_clock, Duration> to a std::tm, without using time_t with the following function:

template <class Duration>
std::tm
make_utc_tm(std::chrono::time_point<std::chrono::system_clock, Duration> tp)
{
    using namespace std;
    using namespace std::chrono;
    typedef duration<int, ratio_multiply<hours::period, ratio<24>>> days;
    // t is time duration since 1970-01-01
    Duration t = tp.time_since_epoch();
    // d is days since 1970-01-01
    days d = round_down<days>(t);
    // t is now time duration since midnight of day d
    t -= d;
    // break d down into year/month/day
    int year;
    unsigned month;
    unsigned day;
    std::tie(year, month, day) = civil_from_days(d.count());
    // start filling in the tm with calendar info
    std::tm tm = {0};
    tm.tm_year = year - 1900;
    tm.tm_mon = month - 1;
    tm.tm_mday = day;
    tm.tm_wday = weekday_from_days(d.count());
    tm.tm_yday = d.count() - days_from_civil(year, 1, 1);
    // Fill in the time
    tm.tm_hour = duration_cast<hours>(t).count();
    t -= hours(tm.tm_hour);
    tm.tm_min = duration_cast<minutes>(t).count();
    t -= minutes(tm.tm_min);
    tm.tm_sec = duration_cast<seconds>(t).count();
    return tm;
}

Also note that the std::chrono::system_clock::time_point on all existing implementations is a duration in the UTC (neglecting leap seconds) time zone. If you want to convert the time_point using another timezone, you will need to add/subtract the duration offset of the timezone to the std::chrono::system_clock::time_point prior to converting it to a precision of days. And if you further want to take leap seconds into account, then adjust by the appropriate number of seconds prior to truncation to days using this table, and the knowledge that unix time is aligned with UTC now.

This function can be verified with:

#include <iostream>
#include <iomanip>

void
print_tm(const std::tm& tm)
{
    using namespace std;
    cout << tm.tm_year+1900;
    char fill = cout.fill();
    cout << setfill('0');
    cout << '-' << setw(2) << tm.tm_mon+1;
    cout << '-' << setw(2) << tm.tm_mday;
    cout << ' ';
    switch (tm.tm_wday)
    {
    case 0:
        cout << "Sun";
        break;
    case 1:
        cout << "Mon";
        break;
    case 2:
        cout << "Tue";
        break;
    case 3:
        cout << "Wed";
        break;
    case 4:
        cout << "Thu";
        break;
    case 5:
        cout << "Fri";
        break;
    case 6:
        cout << "Sat";
        break;
    }
    cout << ' ';
    cout << ' ' << setw(2) << tm.tm_hour;
    cout << ':' << setw(2) << tm.tm_min;
    cout << ':' << setw(2) << tm.tm_sec << " UTC.";
    cout << setfill(fill);
    cout << "  This is " << tm.tm_yday << " days since Jan 1\n";
}

int
main()
{
    print_tm(make_utc_tm(std::chrono::system_clock::now()));
}

Which for me currently prints out:

2013-09-15 Sun 18:16:50 UTC. This is 257 days since Jan 1

In case chrono-Compatible Low-Level Date Algorithms goes offline, or gets moved, here are the algorithms used in make_utc_tm. There are in-depth explanations of these algorithms at the above link. They are well-tested, and have an extraordinarily large range of validity.

// Returns number of days since civil 1970-01-01.  Negative values indicate
//    days prior to 1970-01-01.
// Preconditions:  y-m-d represents a date in the civil (Gregorian) calendar
//                 m is in [1, 12]
//                 d is in [1, last_day_of_month(y, m)]
//                 y is "approximately" in
//                   [numeric_limits<Int>::min()/366, numeric_limits<Int>::max()/366]
//                 Exact range of validity is:
//                 [civil_from_days(numeric_limits<Int>::min()),
//                  civil_from_days(numeric_limits<Int>::max()-719468)]
template <class Int>
constexpr
Int
days_from_civil(Int y, unsigned m, unsigned d) noexcept
{
    static_assert(std::numeric_limits<unsigned>::digits >= 18,
             "This algorithm has not been ported to a 16 bit unsigned integer");
    static_assert(std::numeric_limits<Int>::digits >= 20,
             "This algorithm has not been ported to a 16 bit signed integer");
    y -= m <= 2;
    const Int era = (y >= 0 ? y : y-399) / 400;
    const unsigned yoe = static_cast<unsigned>(y - era * 400);      // [0, 399]
    const unsigned doy = (153*(m + (m > 2 ? -3 : 9)) + 2)/5 + d-1;  // [0, 365]
    const unsigned doe = yoe * 365 + yoe/4 - yoe/100 + doy;         // [0, 146096]
    return era * 146097 + static_cast<Int>(doe) - 719468;
}

// Returns year/month/day triple in civil calendar
// Preconditions:  z is number of days since 1970-01-01 and is in the range:
//                   [numeric_limits<Int>::min(), numeric_limits<Int>::max()-719468].
template <class Int>
constexpr
std::tuple<Int, unsigned, unsigned>
civil_from_days(Int z) noexcept
{
    static_assert(std::numeric_limits<unsigned>::digits >= 18,
             "This algorithm has not been ported to a 16 bit unsigned integer");
    static_assert(std::numeric_limits<Int>::digits >= 20,
             "This algorithm has not been ported to a 16 bit signed integer");
    z += 719468;
    const Int era = (z >= 0 ? z : z - 146096) / 146097;
    const unsigned doe = static_cast<unsigned>(z - era * 146097);          // [0, 146096]
    const unsigned yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365;  // [0, 399]
    const Int y = static_cast<Int>(yoe) + era * 400;
    const unsigned doy = doe - (365*yoe + yoe/4 - yoe/100);                // [0, 365]
    const unsigned mp = (5*doy + 2)/153;                                   // [0, 11]
    const unsigned d = doy - (153*mp+2)/5 + 1;                             // [1, 31]
    const unsigned m = mp + (mp < 10 ? 3 : -9);                            // [1, 12]
    return std::tuple<Int, unsigned, unsigned>(y + (m <= 2), m, d);
}

template <class Int>
constexpr
unsigned
weekday_from_days(Int z) noexcept
{
    return static_cast<unsigned>(z >= -4 ? (z+4) % 7 : (z+5) % 7 + 6);
}

template <class To, class Rep, class Period>
To
round_down(const std::chrono::duration<Rep, Period>& d)
{
    To t = std::chrono::duration_cast<To>(d);
    if (t > d)
        --t;
    return t;
}

Update

More recently I have wrapped the above algorithms up into a freely available date/time library documented and available here. This library makes it very easy to extract a year/month/day from std::system_clock::time_point, and even hours:minutes:seconds:fractional-seconds. And all without going through time_t.

Here is a simple program using the above header-only library to print out the current date and time in the UTC timezone, to the precision of whatever system_clock::time_point offers (in this case microseconds):

#include "date.h"
#include <iostream>


int
main()
{
    using namespace date;
    using namespace std;
    using namespace std::chrono;
    auto const now = system_clock::now();
    auto const dp = time_point_cast<days>(now);
    auto const date = year_month_day(dp);
    auto const time = make_time(now-dp);
    cout << date << ' ' << time << " UTC\n";
}

Which just output for me:

2015-05-19 15:03:47.754002 UTC

This library effectively turns std::chrono::system_clock::time_point into an easy-to-use date-time type.

这篇关于如何转换std :: chrono :: time_point到std :: tm而不使用time_t?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆